今天做的一个题目,给你一个n截得机械臂,每节长度都是固定的100,可以绕每段的节点每次顺时针或者逆时针旋转45度。整个机械臂的下端固定在(0,0),问你最少多少次可以把顶端的半径为10的球完全移到一个给出的矩形区域中。
这题貌似非常暴力的方法(8^10)也能过,不过那样多没意思。开始理解错题意了,以为每段是独立的,只跟每个方向的杆子数目有关了,敲了4层循环枚举枚举杆子的方向和数目,敲完发现不对,下面的节点转动,可以带动上面所有杆子都转动。这样虽然还是只跟每个方向的杆子数目有关,但是就不能分开计算累加得到转移次数了,转移的顺序也不能随便搞。想改暴搜,降不下复杂度,就只好顺着原来的想法去想。
当每种方向的杆子个数确定之后,就可以得到最终球的位置并判断是否在矩形区域内了,目前的关键就是如何才能最快的移动出对应数量的方向。经过相当一段时间才理清楚,当从下向上去转动杆子,当转到一个方向时隔出k个杆子再转,就可以保证某个方向的个数,个数就与最后结果无关了,只与有哪些方向有关;当转动到某一个杆的时候,上面的节点的方向是依赖于当前节点的。然后根据上面的这些,转换成了一个简单的状压dp模型:八个方向,我们通过左转和右转,遍历到我们需要的方向所要的最小次数。比如说我们有五个杆,我们得到要2次向右,2次左上和1次向上,那么我们只要第1个杆不动,第2个杆左转1次,隔2个杆右转3次就可以得到了。很不错的一个题目。
比赛时候的代码,比较挫,8ms:
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 7 inline int ABS(int x){return x<0?-x:x;} 8 9 int dis[1<<8][10]; 10 void dfs(int end, int p){ 11 if(end==0){ 12 dis[end][p]=0; 13 return; 14 } 15 //printf("= %d %d\n", end, p); 16 //getchar(); 17 int res=-1; 18 for(int i=0;i<8;i++){ 19 if(end&(1<<i)){ 20 if(dis[end^(1<<i)][i]==-1) dfs(end^(1<<i), i); 21 int tmp = dis[end^(1<<i)][i]+min((p-i+8)%8,(i-p+8)%8); 22 if(res==-1||tmp<res) res=tmp; 23 } 24 } 25 dis[end][p] = res; 26 } 27 28 int gao(int end){ 29 memset(dis, -1, sizeof(dis)); 30 int ss=end; 31 if(end&(1<<0)) ss^=(1<<0); 32 dfs(ss, 0); 33 return dis[ss][0]; 34 } 35 36 int clear(int a, int b, int c, int d){ 37 int end=0; 38 if(a>0) end|=1<<0; 39 if(c>0) end|=1<<1; 40 if(b>0) end|=1<<2; 41 if(d<0) end|=1<<3; 42 if(a<0) end|=1<<4; 43 if(c<0) end|=1<<5; 44 if(b<0) end|=1<<6; 45 if(d>0) end|=1<<7; 46 return end; 47 } 48 49 int main(){ 50 double sqrt2=sqrt(2.0); 51 52 int t; 53 scanf("%d", &t); 54 while(t--){ 55 int n; 56 scanf("%d", &n); 57 double lx, ly, rx, ry; 58 scanf("%lf%lf%lf%lf", &lx, &ly, &rx, &ry); 59 int ans=-1; 60 for(int a=-n;a<=n;a++){ 61 for(int b=-n;b<=n;b++){ 62 if(ABS(a)+ABS(b)>n) continue; 63 for(int c=-n;c<=n;c++){ 64 if(ABS(a)+ABS(b)+ABS(c)>n) continue; 65 for(int d=-n;d<=n;d++){ 66 if(ABS(a)+ABS(b)+ABS(c)+ABS(d)>n) continue; 67 if((ABS(a)+ABS(b)+ABS(c)+ABS(d)-n)&1) continue; 68 double posx=0; 69 double posy=0; 70 posx+=b*100.0+(c-d)*100.0/sqrt2; 71 posy+=a*100.0+(c+d)*100.0/sqrt2; 72 //printf("%lf %lf\n", posx, posy); 73 if(posx<=rx-10&&posx>=lx+10&&posy<=ry-10&&posy>=ly+10){ 74 int end=0; 75 if(n-(ABS(a)+ABS(b)+ABS(c)+ABS(d))){ 76 for(int i=0;i<4;i++){ 77 end=clear(a,b,c,d)|(1<<i)|(1<<(i+4)); 78 //printf("= %d %d %d %d: %d\n", a,b,c,d, end); 79 int res = gao(end); 80 if(ans==-1||res<ans) ans=res; 81 } 82 }else{ 83 end=clear(a,b,c,d); 84 //printf("%d %d %d %d: %d\n", a,b,c,d, end); 85 int res=gao(end); 86 if(ans==-1||res<ans) ans=res; 87 } 88 } 89 } 90 } 91 } 92 } 93 printf("%d\n", ans); 94 } 95 }