LightOJ1018 Brush (IV)(状压DP)
题目大概说一个平面有n个灰尘,可以用一把刷子直直刷过去清理直线上的所有灰尘,问最少要刷几下才能清理完所有灰尘。
- 首先怎么刷其实是可以确定的,或者说直线有哪些是可以确定的,而最多就有C(n,2)条不一样的直线,即16*15/2=120;
- 然后容易想到用状压DP求解,d[S]表示已经清理的灰尘的状态是S最少刷的次数;
- 而转移就是通过枚举接下来使用那条直线,用我为人人的方式转移,
- 另外直线包含的灰尘集合状态一开始就可以预处理出来,这样时间复杂度O(2n*n2)。
不过超时,超了800多ms。实在想不出怎么没办法。而看了网上,也是一样思路,不过转移是任意选出一个没有在S中的点,然后枚举另一个没有在S的端点,通过添加这两点构成的直线去转移,时间复杂度O(2n*n)。
我表示不解。。这样有些状态会漏吧???
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int d[1<<16],sta[16][16]; 7 int main(){ 8 int t,n,x[16],y[16]; 9 scanf("%d",&t); 10 for(int cse=1; cse<=t; ++cse){ 11 scanf("%d",&n); 12 for(int i=0; i<n; ++i){ 13 scanf("%d%d",x+i,y+i); 14 } 15 if(n==1){ 16 printf("Case %d: %d\n",cse,1); 17 continue; 18 } 19 for(int i=0; i<n; ++i){ 20 for(int j=0; j<n; ++j){ 21 if(i==j){ 22 sta[i][j]=(1<<i); 23 continue; 24 } 25 int s=0; 26 for(int k=0; k<n; ++k){ 27 if((x[i]-x[j])*(y[j]-y[k])==(x[j]-x[k])*(y[i]-y[j])){ 28 s|=(1<<k); 29 } 30 } 31 sta[i][j]=s; 32 } 33 } 34 memset(d,127,sizeof(d)); 35 d[0]=0; 36 for(int i=0; i<(1<<n)-1; ++i){ 37 if(d[i]>10000) continue; 38 int j=0; 39 while(j<n){ 40 if((i>>j&1)==0) break; 41 ++j; 42 } 43 for(int k=0; k<n; ++k){ 44 if((i>>k&1)==0){ 45 d[i|sta[j][k]]=min(d[i|sta[j][k]],d[i]+1); 46 } 47 } 48 } 49 printf("Case %d: %d\n",cse,d[(1<<n)-1]); 50 } 51 return 0; 52 }