搜索
开篇:
mayan游戏(noip2011 day1 T3)
这道题就是个码量题,老师讲题时淡淡的说写完前两题就花一个半小时了,最后一题不快点打会调不出来,对于一个三个半小时就写两题的蒟蒻来说这。。。。这题就是老师口中的简单题。。。。
这种消数游戏应该每个人都玩过,顾名思义,要消数,所以就要打一个消数函数,消完数,他上面的数又不能在空中飘着(他又不是蜘蛛侠),所以还要打一个下移函数,由于这两天玩洛谷版的2048,得出一个结论,下面的被消掉后,上面的掉下来若能消还会继续消,直到不能消为止,所以还要一个判断有没有消完的函数,这题一看就知道要DFS,所以DFS怎么可以少。
但这题空间就128MB,时间也是巨卡,没有优化怎么能过?DFS这个东西十分神奇,因为他可以莫名其妙的剪枝(剪得你都不认识他了)
然后,你就可以打出这样的代码,一遍不过就重打吧。。。。。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<algorithm> 6 using namespace std; 7 const int maxn=10; 8 int mayan[maxn][maxn]; 9 int xxx; 10 int ans[maxn][maxn]; 11 int n; 12 inline int is_clear() 13 { 14 for(int i=0;i<5;i++) 15 { 16 for(int j=0;j<7;j++) 17 { 18 if(mayan[i][j]) 19 { 20 return 0; 21 } 22 } 23 } 24 return 1; 25 } 26 void new_ans(int i,int x,int y,int flag) 27 { 28 ans[i][1]=x; 29 ans[i][2]=y; 30 ans[i][3]=flag; 31 } 32 void fell_down(int x) 33 { 34 int tot=-1; 35 for(int i=0;i<7;i++) 36 { 37 if(mayan[x][i]) 38 { 39 mayan[x][++tot]=mayan[x][i]; 40 } 41 } 42 for(int i=tot+1;i<7;i++) 43 { 44 mayan[x][i]=0; 45 } 46 return ; 47 } 48 void clear() { 49 bool flag = true; 50 while(flag) { 51 flag = false; 52 int temp[10][10]; 53 memcpy(temp,mayan,sizeof(temp)); 54 for(int i=0;i<5;i++) 55 for(int j=0;j<7;j++) 56 { 57 if(i >= 1 && i <= 3 && temp[i][j] && temp[i][j] == temp[i-1][j] && temp[i][j] == temp[i+1][j]) { 58 mayan[i][j] = 0; 59 mayan[i-1][j] = 0; 60 mayan[i+1][j] = 0; 61 flag = true; 62 } 63 if(j >= 1 && j <= 5 && temp[i][j] && temp[i][j] == temp[i][j-1] && temp[i][j] == temp[i][j+1]) { 64 mayan[i][j] = 0; 65 mayan[i][j-1] = 0; 66 mayan[i][j+1] = 0; 67 flag = true; 68 } 69 } 70 71 if(!flag) 72 return; 73 for(int i=0;i<5;i++) 74 fell_down(i); 75 } 76 } 77 void dfs(int x) 78 { 79 if(is_clear()&&x==n) 80 { 81 for(int i=1;i<=x;i++) 82 { 83 cout<<ans[i][1]<<" "<<ans[i][2]<<" "<<ans[i][3]<<endl; 84 xxx=1; 85 } 86 exit(0); 87 } 88 if(x>=n) 89 { 90 return ; 91 } 92 int temp[10][10]; 93 memcpy(temp,mayan,sizeof(temp)); 94 for(int i=0;i<5;i++) 95 { 96 for(int j=0;j<7;j++) 97 { 98 if(!mayan[i][j]) 99 { 100 continue; 101 } 102 if(i!=4) 103 { 104 swap(mayan[i][j],mayan[i+1][j]); 105 fell_down(i); 106 fell_down(i+1); 107 clear(); 108 new_ans(x+1,i,j,1); 109 dfs(x+1); 110 new_ans(x+1,0,0,0); 111 memcpy(mayan,temp,sizeof(mayan)); 112 } 113 if(i&&!mayan[i-1][j]) 114 { 115 swap(mayan[i][j],mayan[i-1][j]); 116 fell_down(i); 117 fell_down(i - 1); 118 clear(); 119 new_ans(x+1,i,j,-1); 120 dfs(x + 1); 121 new_ans(x+1,0,0,0); 122 memcpy(mayan,temp,sizeof(mayan)); 123 } 124 } 125 126 } 127 } 128 int main() 129 { 130 cin>>n; 131 for(int i=0;i<5;i++) 132 { 133 int p=0; 134 do 135 { 136 cin>>mayan[i][p]; 137 p++; 138 }while(mayan[i][p-1]!=0); 139 } 140 dfs(0); 141 if(xxx!=1) 142 { 143 cout<<-1<<endl; 144 } 145 }
拓展:剪枝
- DFS专用,BFS就不用想了。
- 可行性剪枝:如果已经判断这下面的都不合法,就可以剪枝。
- 最优性剪枝:如果下面的答案一定不会比当前更优,剪枝
- 搜索的顺序对剪枝会有很大影响。
一些好到无话可说的好题目:
- 如果当前的出牌数已经超过了最优的ans,剪枝(最优性剪枝)
- 搜索顺序:先顺子,之后就不用记录牌的大小了,只要记录张数有1,2,3,4的分别有几种就可以了。
- 之后先打牌数多的,先打四带二,再打三带一,也就是说一旦打出三代一,以后就不可能打出四带二了。
- 每次搜索开始时,用当前的出牌数加上不同点数的牌的数量更新ans。
代码
2.切蛋糕(IOI 1999)
这是道名题啊!!!!!
IOI啊!!!!
不过也确实时20年前的题目了,要是放到现在,那想游客这样的大佬也不会天天说AKIOI了,但在当年确实怪难,那剪枝,鬼畜。
- 搜索顺序 :从最下面一层开始,一层一层向上搜,枚举最下面的半径和高度最大。
- 以算好的答案(最小面积)减去蛋糕当前层以下的总面积若小于上面所能构成的最小面积,就剪枝(最优性剪枝)
- 总体积减去当前层以下的总体积小于上面所能构成的最小体积,剪枝(可行性剪枝)
3.小木棍(加强版)
这是一个好题目 ,那个天天AKIOI的游客(因为是机房大佬,所以不得不膜)竟然运用运动会的时间,花了一下午没打出来,真好奇他当时有没有对电脑进行了某种报复行为。。。。
- 暴力思路,一个一个枚举长度x,看能不能填满sum/x根木棍。
- 剪枝1:X必须是sum的因数,且X>=maxlen。
- 剪枝2:将木棍降序排列,优先填更长的。