@wwwqeqeqeqe
2019-05-05T10:12:08.000000Z
字数 9441
阅读 945
搜索
A 生日蛋糕(POJ 1190)
题目:
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。 令Q = Sπ 请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。(除Q外,以上所有数据皆为正整数)
解题思路:
因为题目说了所有数据都是整数,所以,我们可以通过穷举法来进行操作。因为我们的蛋糕一共有m层,而且从上往下是一定递增的。那么,从第1、2、3、······、m层的最小半径和高度都是当前层数的数值。因此,我们就找到了下界。同时,因为n最大为10000,当h=1时,r最大为100,当r=1时,h最大为10000,因此我们找到了上界。我们可以通过枚举来进行查找。
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;const int INF=0x3f3f3f3f;int n,m;int ans;void dfs(int t,int sumS,int sumV,int r,int h){if(t == 0){if(sumV == n)ans=min(sumS,ans);return;}for(int i=r-1;i>=t;i--){if(t == m)sumS = i*i;int mh=h-1;for(int j=mh;j>=t;j--){dfs(t-1,sumS+i*j*2,sumV+i*i*j,i,j);}}}int main(){cin >> n >> m;ans=INF;dfs(m,0,0,100,10000);if(ans == INF)ans=0;cout << ans << endl;}
我们就通过题目大意得到了这样一个代码。但是,很显然,如果我们直接交上去,肯定会TLE,因为我们这个代码是没有进行剪枝的。那要如果进行剪枝呢?首先我们知道,我们要求的是最小的表面积,那么,当我们在计算过程中得到的表面积已经比我们得到的最小表面积大了,那么,这个答案肯定不是我们需要的那个答案,可以直接return。同时,当我们的体积已经比n大的时候,很显然我们最后的结果sumV!=n,也直接return。同时,因为2*r*h=S,r*r*h=V,那么,S=V*2/r,r越大,S越小。所以,我们把剩下的体积(n-sumV)全部只用来做成一个圆柱体,他所增加的表面积是最小的。所以,当我们把这个剩余体积全部换成表面积加上之前的表面积大于ans的话,也是直接return掉的。于是,我们得到了最后的代码。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;const int INF=0x3f3f3f3f;int n,m;int ans;int mnS[50],mnV[50];void dfs(int t,int sumS,int sumV,int r,int h){if(t == 0){if(sumV == n)ans=min(sumS,ans);return;}if(sumS+mnS[t] > ans || sumV+mnV[t] > n)return;if(2*(n-sumV)/r+sumS >=ans)return;for(int i=r-1;i>=t;i--){if(t == m)sumS = i*i;int mh=h-1;for(int j=mh;j>=t;j--){dfs(t-1,sumS+i*j*2,sumV+i*i*j,i,j);}}}int main(){mnS[0]=0;mnV[0]=0;for(int i=1;i<22;i++){mnS[i]=mnS[i-1]+i*i*2;mnV[i]=mnV[i-1]+i*i*i;}cin >> n >> m;ans=INF;dfs(m,0,0,100,10000);if(ans == INF)ans=0;cout << ans << endl;}
B Eight(POJ 1077)
题目大意:
题目给你一个3X3的格子,里面有1~8 八个数以及一个字母X,每次将X和旁边的一个数交换,问是否能够得到最后的序列使得图形满足从左往右,从上往下依次为1~8,最后一格为X。如果存在,则输出X运动的轨迹。如果没有,输出unsolvable。
解题思路:
因为一共只有9个格子,我们可以通过康拓展开获取当前状态的一个hash值,接着使用BFS对状态进行搜索,在搜索的过程中保存状态的转移过程。当我们的hash值为1的时候,那么我们就找到了我们需要的答案,return true即可。如果我们没有找到满足条件的答案,则return false。最后根据返回结果来进行答案的输出。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<queue>using namespace std;const int maxn=1e6+5;const int dx[]={-1,1,0,0};const int dy[]={0,0,-1,1};const int fac[]={1,1,2,6,24,120,720,5040,40320,362880};const char d[]={'u','d','l','r'};char path[maxn];int pre[maxn];int vis[maxn];char str[55];struct node{int status;int s[15];int loc;};int cantor(int s[]){int sum=0;for(int i=0;i<9;i++){int num=0;for(int j=i+1;j<9;j++){if(s[j]<s[i])num++;}sum+=num*fac[8-i];}return sum+1;}bool bfs(node now){memset(vis,0,sizeof(vis));queue<node>q;now.status=cantor(now.s);pre[now.status]=-1;vis[now.status]=1;node t;q.push(now);while(q.size()){now=q.front();q.pop();if(now.status == 1)return true;int x=now.loc/3;int y=now.loc%3;for(int i=0;i<4;i++){int xx=x+dx[i];int yy=y+dy[i];if(xx>=0 && xx<=2 && yy>=0 && yy<=2){t=now;t.s[x*3+y]=t.s[xx*3+yy];t.s[xx*3+yy]=9;t.status=cantor(t.s);if(!vis[t.status]){vis[t.status]=1;t.loc=xx*3+yy;pre[t.status]=now.status;path[t.status]=d[i];q.push(t);}}}}return false;}void Print(int status){if(pre[status] == -1)return;Print(pre[status]);cout << path[status] ;}int main(){scanf("%[^\n]s",str);node now;int cnt=0;for(int i=0;str[i]!='\0';i++){if(str[i] == ' ')continue;if(str[i] == 'x'){now.s[cnt]=9;now.loc=cnt++;}else{now.s[cnt++]=str[i]-'0';}}if(!bfs(now)){cout << "unsolvable" <<endl;}else{Print(1);cout <<endl;}return 0;}
C Robot(POJ 1376)
题目大意:
给出一张图,图中有一个机器人,给出机器人的起点和终点以及初始朝向,算出机器人从起点左上角到达终点左上角的最小花费时间。每花费一个单位时间,可以朝当前方向前进1~3格,或者向左或向右旋转一次。
解题思路:
因为题目给出的地图是按照格子给出的,但是机器人是在方格的线上进行移动的,因此,在判断当前方格是否有障碍时需要注意判断一下,另外,地图边界也不能走。其他的就和普通BFS一样做。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<queue>using namespace std;const int maxn=1e2+5;const int dx[]= {-1,0,1,0};const int dy[]= {0,1,0,-1};struct node{int x,y;int time;int pos;};int n,m,mp[maxn][maxn];int vis[maxn][maxn][5];int getpos(char *str){if(!strcmp(str,"north"))return 0;if(!strcmp(str,"east"))return 1;if(!strcmp(str,"south"))return 2;return 3;}bool check(int x,int y){if(x<1||x>=n||y<1||y>=m)return false;if(mp[x][y]||mp[x+1][y]||mp[x][y+1]||mp[x+1][y+1])return false;return true;}int main(){while(cin >> n >> m &&(n || m)){for(int i=1; i<=n; i++){for(int j=1; j<=m; j++){cin >> mp[i][j];}}node bg,ed;memset(vis,0,sizeof(vis));cin >> bg.x >> bg.y;cin >> ed.x >> ed.y;char op[25];cin >> op;bg.pos=getpos(op);bg.time=0;queue<node> a;a.push(bg);vis[bg.x][bg.y][bg.pos]=1;int ans=-1;if(bg.x == ed.x && bg.y == ed.y){cout << 0 << endl;continue;}if(!check(ed.x,ed.y)){cout << -1 << endl;continue;}while(a.size()){node aa=a.front();a.pop();int xx=aa.x;int yy=aa.y;for(int i=1; i<=3; i++){xx+=dx[aa.pos];yy+=dy[aa.pos];if(!check(xx,yy))break;if(xx == ed.x && yy==ed.y){ans=aa.time+1;break;}if(!vis[xx][yy][aa.pos]){node ab;ab.x=xx;ab.y=yy;ab.time=aa.time+1;ab.pos=aa.pos;vis[xx][yy][ab.pos]=1;a.push(ab);}}for(int i=0; i<4; i++){if(max(aa.pos,i)-min(aa.pos,i) == 2)continue;if(vis[aa.x][aa.y][i])continue;node ab=aa;ab.time=aa.time+1;ab.pos=i;vis[ab.x][ab.y][ab.pos]=1;a.push(ab);}if(ans!=-1)break;}cout << ans << endl;}return 0;}
D Pushing Boxes (POJ 1475)
题目大意:
题目给出一张地图,地图里面给出人的位置,箱子的位置以及箱子到达的目的地。让你设计一种方案,是我们可以推最少次数的箱子让箱子到达目的地。如果没有能将箱子推向目的地的方案,则输出Impossible.,否则,在自己单独行动的时候输出小写的方位,在推箱子的时候输出大写的方位。
解题思路:
我们优先对箱子进行BFS,每移动一次箱子,就对人进行一次BFS,找到人从当前路径走到推箱子的下一步所需要的位置的最短路径。这个位置的具体位置通过箱子对应的下一步移动的相反方向来查找,具体在箱子移动方向相反位置的那个格子内,如果相反方向是墙或者人到不了的地方,就返回false,进而这次箱子的移动无效。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<queue>#include<string>using namespace std;const int dx[]= {-1,0,1,0};const int dy[]= {0,1,0,-1};const char dpathB[4] = {'N', 'E', 'S', 'W'};const char dpathP[4] = {'n', 'e', 's', 'w'};int n,m;char mp[25][25];int sx, sy;int bx, by;string ans;struct person{int x,y;string path;};struct status{int px,py,bx,by;string path;};bool check(int x, int y){if(x<0 || x>=n || y<0 || y>=m)return false;if(mp[x][y]=='#')return false;return true;}bool bfs2(int ppx, int ppy, int bbx, int bby, int kx, int ky, string &path){int vis[25][25];memset(vis, 0, sizeof(vis));vis[ppx][ppy] = 1;vis[kx][ky] = 1;person first;first.x = ppx;first.y = ppy;first.path = "";queue<person> myPerson;myPerson.push(first);while(!myPerson.empty()){person now = myPerson.front();myPerson.pop();if(now.x==bbx && now.y==bby){path = now.path;return true;}for(int i=0; i<4; i++){int zx = now.x + dx[i];int zy = now.y + dy[i];if(check(zx, zy) && !vis[zx][zy]){vis[zx][zy] = 1;person next;next.x = zx;next.y = zy;next.path = now.path + dpathP[i];myPerson.push(next);}}}return false;}bool bfs(){int vis[25][25];memset(vis, 0, sizeof(vis));status first;first.bx = bx;first.by = by;first.px = sx;first.py = sy;first.path = "";vis[bx][by] = 1;queue<status> myPath;myPath.push(first);while(!myPath.empty()){status now = myPath.front();myPath.pop();for(int i=0; i<4; i++){int bbx = now.bx + dx[i];int bby = now.by + dy[i];int tx = now.bx - dx[i];int ty = now.by - dy[i];string path = "";if(check(bbx, bby) && check(tx, ty) && !vis[bbx][bby]){if(bfs2(now.px, now.py, tx, ty, now.bx, now.by, path)){vis[bbx][bby] = 1;status next;next.bx = bbx;next.by = bby;next.px = now.bx;next.py = now.by;next.path = now.path + path + dpathB[i];if(mp[bbx][bby]=='T'){ans = next.path;return true;}myPath.push(next);}}}}return false;}int main(){int cc = 0;while(cin >> n >> m){ans = '\0';cc++;getchar();memset(mp, 0, sizeof(mp));if(n==0 && m==0) break;for(int i=0; i<n; ++i){scanf("%s", mp[i]);getchar();for(int j=0; j<m; j++){if(mp[i][j]=='S'){sx = i;sy = j;}if(mp[i][j]=='B'){bx = i;by = j;}}}cout << "Maze #" << cc << endl;if(bfs())cout << ans << endl;elsecout << "Impossible." << endl;cout << endl;}return 0;}
E Dearboy's Puzzle (POJ 2308)
题目大意:
题目给出一个地图,里面有ABCD和*五种字符,*表示为空,ABCD表示有四种卡片,然后对这四种卡片进行连连看消除。最后问给出的地图能否通过连连看消除所有的ABCD。
解题思路:
首先统计ABCD四种牌的张数,如果存在某种牌有奇数个,那么无解。接着对地图外层进行DFS查找,指定一张牌来想办法把这张牌消除。再通过BFS以指定的这张牌为起点,查询能够和他配对的牌。找到后,再枚举这张牌与哪一张牌相消或者当前这张牌不消。在消除的过程中,需要单独判断AB这种情况。
BA
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<queue>using namespace std;const int dx[]= {1,-1,0,0};const int dy[]= {0,0,1,-1};int mp[12][12],n,m,num[5],sum;bool flag,vis[11][11];char str[12];struct Node{int x,y,turn,d;Node() {}Node(int a,int b,int c,int dd){x = a;y = b;turn = c;d = dd;}};void Init(){memset(mp,0,sizeof mp);memset(num,0,sizeof num);sum = flag = 0;}bool inarea(int x,int y){if(x<1||y<1||x>n||y>m) return false;else return true;}void bfs(int x,int y,int w,Node *can,int &len){memset(vis,0,sizeof vis);queue<Node> Q;Q.push(Node(x,y,0,-1));vis[x][y] = 1;while(!Q.empty()){Node u = Q.front();Q.pop();if(mp[u.x][u.y] == w){can[++len].x = u.x;can[len].y = u.y;continue;}for(int i = 0; i < 4; i++){int tx = u.x+dx[i],ty = u.y+dy[i],tturn = 0;if(!inarea(tx,ty) || (mp[tx][ty] != -1&&mp[tx][ty] != w) || vis[tx][ty]) continue;if(u.d == i||u.d == -1)tturn = u.turn;else tturn = u.turn+1;if(tturn > 2) continue;vis[tx][ty] = 1;Q.push(Node(tx,ty,tturn,i));}}}bool check(){for(int i = 1; i < n; i++)for(int j = 1; j < m; j++){if(mp[i][j] != -1&&mp[i][j+1] != -1&&mp[i][j] != mp[i][j+1]){if(mp[i][j] == mp[i+1][j+1]&&mp[i][j+1] == mp[i+1][j]&&num[mp[i][j]] == 2&&num[mp[i][j+1]] == 2)return false;}}return true;}void dfs(int cur){if(cur == 0){flag = 1;return;}if(flag) return;if(!check()) return;for(int i = 1; i <= n; i++)for(int j = 1; j <= n; j++){if(flag) return;if(mp[i][j] != -1){int w = mp[i][j],len = 0;Node can[110];mp[i][j] = -1;bfs(i,j,w,can,len);for(int k = 1; k <= len; k++){mp[can[k].x][can[k].y] = -1;num[w] -= 2;dfs(cur-2);num[w] += 2;mp[can[k].x][can[k].y] = w;}mp[i][j] = w;}}}int main(){while(scanf("%d%d",&n,&m) != EOF&&n+m){Init();for(int i = 1; i <= n; i++){scanf("%s",str+1);for(int j = 1; j <= m; j++){if(str[j] == '*') mp[i][j] = -1;else mp[i][j] = str[j]-'A'+1,num[str[j]-'A'+1]++;}}sum = num[1]+num[2]+num[3]+num[4];if(num[1]%2||num[2]%2||num[3]%2||num[4]%2){printf("no\n");continue;}dfs(sum);if(flag) printf("yes\n");else printf("no\n");}}