A.解救小Q
BFS。每次到达一个状态时看是否是在传送阵的一点上,是则传送到另一点即可。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define NA 100007 char mp[52][52]; struct status { int x,y; int l; }; int vis[52][52]; struct transport { int x1,y1; //初始点 int x2,y2; //传送目标点 int flag; }a[27]; int n,m; int head,tail; int pathx[4]={0,1,0,-1}; int pathy[4]={-1,0,1,0}; bool OK(int nx,int ny) { return nx>=0&&ny>=0&&nx<n&&ny<m; } int bfs(int x,int y) { status now,next; queue<status> que; now.x=x; now.y=y; now.l=0; vis[now.x][now.y]=1; que.push(now); while(!que.empty()) //队列非空 { now = que.front(); que.pop(); for(int i=0;i<4;i++) { int nx=now.x+pathx[i]; int ny=now.y+pathy[i]; if(vis[nx][ny] || !OK(nx,ny)) continue; if(mp[nx][ny]=='Q') return now.l+1; if(mp[nx][ny]!='#') { if(mp[nx][ny]>='a'&&mp[nx][ny]<='z') { int h=mp[nx][ny]-'a'; if(a[h].x1!=nx||a[h].y1!=ny) //初始点和目标点可以互换 { next.x=a[h].x1; next.y=a[h].y1; } else { next.x=a[h].x2; next.y=a[h].y2; } next.l=now.l+1; que.push(next); } else { next.x=nx; next.y=ny; next.l=now.l+1; que.push(next); } vis[nx][ny]=1; } } } return -1; } int main() { int T,x,y; int N,M; scanf("%d",&T); while(T--) { memset(vis,0,sizeof(vis)); memset(mp,0,sizeof(mp)); for(int j=0;j<27;j++) a[j].flag=0; scanf("%d%d",&N,&M); n=N; m=M; getchar(); for(int i=0;i<N;i++) { for(int j=0;j<M;j++) { scanf("%c",&mp[i][j]); if(mp[i][j]=='L') { x=i; y=j; } if(mp[i][j]>='a'&&mp[i][j]<='z') { int h=mp[i][j]-'a'; if(a[h].flag==0) { a[h].x1=i; a[h].y1=j; a[h].flag=1; } else if(a[h].flag==1) { a[h].x2=i; a[h].y2=j; } } } getchar(); } printf("%d\n",bfs(x,y)); } return 0; }
B.方老师开橙卡
每次枚举一位数,从1位数字到9位数字,满足条件则加入队列。比如21,首先1*1 = 1 和 9*9 = 81 都满足个位与21的个位相同,加入队列,下次就考虑匹配第二位,然后第三位,。。每次去除相同位数的数字(que.size()),比如去除Y,则拓展的时候枚举X,这时m=10*X+Y,看此时m^2的那一位是否与n的那一位匹配,如果匹配,则加入队列。每次如果相等,顺便判断一下是否就是n,就是的话直接与答案相比,小于答案则更新答案。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #define Mod 1000000007 using namespace std; #define N 50007 int Scheck(ll ka,ll n) { while(ka && n) { if(ka%10 != n%10) return 0; ka/=10; n/=10; } if(n == 0) return 1; else return 0; } ll Ten(int wei) { ll res = 1; for(int i=0;i<wei;i++) res *= 10; return res; } ll toTen(ll tmp) { ll res = 1; while(tmp) { res *= 10; tmp /= 10; } return res; } ll ans; int w[12],ind; void BFS(ll n) { queue<ll> que; ll m,x,y; for(y=0;y<=9;y++) { ll ka = y*y; if(ka%10 == w[1]) { if(Scheck(ka,n)) { if(y < ans) ans = y; } else que.push(y); } } int layer = 1; while(!que.empty()) { layer++; if(layer > 10 || layer >= ind) return; ll ten = Ten(layer-1); int qsize = que.size(); while(qsize--) { ll tmp = que.front(); que.pop(); for(x=0;x<=9;x++) { m = ten*x+tmp; ll m2 = m*m; if((m2/ten)%10 == w[layer]) { if(Scheck(m2,n)) { if(m < ans) ans = m; } else que.push(m); } } } } return; } int main() { int T; ll n; scanf("%d",&T); while(T--) { scanf("%lld",&n); if(n == 0) { printf("0\n"); continue; } ll kn = n; ind = 1; while(kn) { w[ind++] = kn%10; kn/=10; } ans = Mod; BFS(n); if(ans == Mod) puts("None"); else printf("%lld\n",ans); } return 0; }
C.贪吃蛇
如果存储每节身体的坐标,会爆Memory的。
考虑因为每个相邻节的坐标都是相邻的,维护一个节与前一个节的关系即可,有四种情况,可以分别用00,01,10,11来表示,这样的话就只需维护蛇头的坐标,根据01关系就可推断出整个蛇身的位置。然后就是BFS了,注意不要咬到蛇身和往自己来的方向走(如果长度为1的话就可以)。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #define Mod 1000000007 #define ll long long using namespace std; #define N 107 struct Point { int x,y; }p[11]; struct node { int x,y; int num,step; }S; char mp[17][17]; int vis[70000][16][16]; int R,C,K; int EX,EY; int dx[4] = {1,0,0,-1}; int dy[4] = {0,1,-1,0}; int p_to_num(Point *p) { int i,k; int res = 0,now = 0; for(i=1;i<K;i++) { int sx = p[i].x; int sy = p[i].y; int tx = p[i+1].x; int ty = p[i+1].y; if(sx > tx && sy == ty) k = 3; //下 else if(sx == tx && sy > ty) k = 2; //右 else if(sx < tx && sy == ty) k = 0; //上 else if(sx == tx && sy < ty) k = 1; //左 k <<= now; now += 2; res |= k; } return res; } bool OK(int nx,int ny) { if(nx >= 0 && nx < R && ny >= 0 && ny < C) return true; return false; } bool check(int num) { int i,j,k; k = K-1; int x = 0,y = 0; while(k--) { x += dx[num&3]; y += dy[num&3]; if(x == 0 && y == 0) return false; num >>= 2; } return true; } int BFS(node S) { int i,j,k; queue<node> que; memset(vis,0,sizeof(vis)); que.push(S); vis[S.num][S.x][S.y] = 1; int MOD = (1<<((K-1)*2)); while(!que.empty()) { node tmp = que.front(); que.pop(); if(tmp.x == EX && tmp.y == EY) return tmp.step; for(i=0;i<4;i++) { if(K <= 1 || ((tmp.num&3) != i)) { int nx = tmp.x + dx[i]; int ny = tmp.y + dy[i]; if(!OK(nx,ny) || mp[nx][ny] == '#') continue; node now; now.num = (tmp.num << 2); now.num |= (3-i); now.num %= MOD; if(vis[now.num][nx][ny]) //状态已走过 continue; if(!check(now.num)) //咬到蛇身 continue; vis[now.num][nx][ny] = 1; now.x = nx; now.y = ny; now.step = tmp.step + 1; que.push(now); } } } return -1; } int main() { int i,j,k,cs = 1; while(scanf("%d%d",&R,&C)!=EOF) { K = 1; for(i=0;i<R;i++) { scanf("%s",mp[i]); for(j=0;j<C;j++) { if(mp[i][j] == '@') EX = i,EY = j; if(mp[i][j] >= '1' && mp[i][j] <= '9') { k = mp[i][j] - '0'; p[k].x = i,p[k].y = j; K = max(K,k); } } } S.num = p_to_num(p); S.x = p[1].x; S.y = p[1].y; S.step = 0; printf("Case #%d: %d\n",cs++,BFS(S)); } return 0; }
D.方老师与素数
BFS搜索。每次改变一位(千位,百位,十位或个位),拓展状态,直到达到目标状态。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <cstdlib> #include <queue> #define Mod 1000000007 using namespace std; #define N 10007 int u[N],c,vis[N],prime[N]; int n,a,b,flag; struct node { int num,step; }S,E; void doit() { int i,j; c=0; for(i=2;i<=10003;i++) u[i] = 1; for(i=2;i<=10003;i++) { if(u[i]) prime[c++] = i; for(j=0;j<c&&i*prime[j]<=10003;j++) { u[i*prime[j]] = 0; if(i%prime[j] == 0) break; } } } int change(int base,int num,int ind) { char ss[5]; ss[0] = base/1000 + 48; ss[1] = (base/100)%10 + 48; ss[2] = (base/10)%10 + 48; ss[3] = base%10 + 48; ss[ind] = num + 48; ss[4] = '\0'; return atoi(ss); } int bfs(node S,int b) { queue<node> que; node k; while(!que.empty()) que.pop(); int i,j,res = 0; int ka = 1; flag = 0; memset(vis,0,sizeof(vis)); que.push(S); vis[S.num] = 1; while(!que.empty()) { node tmp = que.front(); que.pop(); if(tmp.num == b) { flag = 1; return tmp.step; } for(i=1;i<=9;i++) //千位 { int now = change(tmp.num,i,0); if(now != tmp.num && u[now] && !vis[now]) { vis[now] = 1; k.num = now; k.step = tmp.step + 1; que.push(k); } } for(i=1;i<=3;i++) //百,十,个位 { for(j=0;j<=9;j++) { int now = change(tmp.num,j,i); //i位变为j if(now != tmp.num && u[now] && !vis[now]) { vis[now] = 1; k.num = now; k.step = tmp.step + 1; que.push(k); } } } } return res; } int main() { int t,i,a,b; doit(); scanf("%d",&t); while(t--) { scanf("%d%d",&a,&b); S.num = a; S.step = 0; int res = bfs(S,b); if(!flag) puts("Impossible"); else printf("%d\n",res); } return 0; }
E.Sticks
经典题了。
DFS+剪枝
将木棍大小从大到小排序,DFS中维护参数:
ind:当前枚举到第ind根棒子
left:要组成LEN还需left长度
remain:剩下的总长度为remain
然后剪枝,剪枝见注释。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 107 int a[N],vis[N]; int S,LEN,n; int DFS(int ind,int left,int remain) //当前枚举到第ind根棒子,要组成LEN还需left长度,剩下的总长度为remain { int i; if(remain == LEN) return 1; for(i=ind;i<n;i++) { if(!vis[i] && a[i] <= left) { vis[i] = 1; //用这根棒子 if(a[i] < left && DFS(i+1,left-a[i],remain-a[i])) return 1; else if(a[i] == left && DFS(0,LEN,remain-a[i])) return 1; vis[i] = 0; //都没成功,这根不能用 if(remain == S || a[i] == left || left == LEN) //如果一根都没用,肯定找不到了,如果不能找到一根长度为LEN的木棍,以后也不能找到了,如果这根木棍长度等于剩下的,但是DFS没成功,说明不可能找到一根或多根将其凑齐了 return 0; while(i+1 < n && a[i] == a[i+1]) //相同的木棍,不成功就都不成功 i++; } } return 0; } int cmp(int ka,int kb) { return ka > kb; } int main() { while(scanf("%d",&n)!=EOF && n) { S = 0; for(int i=0;i<n;i++) { scanf("%d",&a[i]); S += a[i]; } sort(a,a+n,cmp); for(LEN=a[0];LEN<=S;LEN++) { if(S%LEN == 0) { memset(vis,0,sizeof(vis)); if(DFS(0,LEN,S)) { printf("%d\n",LEN); break; } } } } return 0; }
F.方老师与迷宫
三维的BFS,常规写法,没什么好说的。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define N 100007 char mp[32][32][32]; char ss[32]; int vis[32][32][32]; struct node { int x,y,z; int step; }Sa,Ea; int sx[7] = {1,-1,0,0,0,0}; int sy[7] = {0,0,1,-1,0,0}; int sz[7] = {0,0,0,0,1,-1}; int L,R,C; bool OK(int nx,int ny,int nz) { if(nx >= 0 && nx < R && ny >= 0 && ny < C && nz >= 0 && nz < L) return true; return false; } int bfs(node S,node E) { queue<node> que; while(!que.empty()) que.pop(); que.push(S); memset(vis,0,sizeof(vis)); vis[S.x][S.y][S.z] = 1; while(!que.empty()) { node tmp = que.front(); que.pop(); if(tmp.x == E.x && tmp.y == E.y && tmp.z == E.z) return tmp.step; for(int i=0;i<6;i++) { node now; now.x = tmp.x + sx[i]; now.y = tmp.y + sy[i]; now.z = tmp.z + sz[i]; if(!OK(now.x,now.y,now.z) || mp[now.z][now.x][now.y] == '#' || vis[now.x][now.y][now.z]) continue; now.step = tmp.step + 1; vis[now.x][now.y][now.z] = 1; que.push(now); } } return -1; } int main() { int i,j,k; while(scanf("%d%d%d",&L,&R,&C)!=EOF && (L&&R&&C)) { for(i=0;i<L;i++) { for(j=0;j<R;j++) { scanf("%s",mp[i][j]); for(k=0;k<C;k++) { if(mp[i][j][k] == 'S') Sa.x = j,Sa.y = k,Sa.z = i,Sa.step = 0; else if(mp[i][j][k] == 'E') Ea.x = j,Ea.y = k,Ea.z = i,Ea.step = Mod; } } getchar(); } //printf("%d %d %d\n%d %d %d\n",S.x,S.y,S.z,E.x,E.y,E.z); int res = bfs(Sa,Ea); if(res != -1) printf("Escaped in %d minute(s).\n",res); else puts("Trapped!"); } return 0; }
G.Eight Puzzle
把空格看做9,利用Cantor展开保存状态,BFS做,每次通过空格的移动来扩展状态,如果状态没被访问过,则加入队列。
Cantor展开(康托展开)可以求一个排列在所有这几个数的排列中的位置:
把一个整数X展开成如下形式:
a(i)为i右边的比a(i)小的数的个数。
比如1324是{1,2,3,4}的所有排列的第三个,说明比1324小的排列有2个,0*3!+1*2!+0*1!+0*0!=2
这样可以方便的记录状态。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define N 370000 struct node { int puzzle[3][3]; int step; }; int hash[N],vis[N]; int a[10],b[3][3]; char ss[30]; int mp[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; int sx[4] = {1,0,-1,0}; int sy[4] = {0,1,0,-1}; int fact[11] = {0,1,2,6,24,120,720,5040,40320,362880,3628800}; int Cantor(int *a) //康托展开 { int i,j,cnt; int res = 0; for(i=0;i<9;i++) { cnt = 0; for(j=i+1;j<9;j++) if(a[i] > a[j]) cnt++; res += cnt*fact[9-i-1]; } return res; } void DB_to_SG(int *a,int b[3][3]) //二维转一维 { int i,j,k = 0; for(int i=0;i<3;i++) for(j=0;j<3;j++) a[k++] = b[i][j]; } int HS(int b[3][3]) //取得排列的Cantor展开的值 { int i,j,k,res; DB_to_SG(a,b); return Cantor(a); } bool OK(int nx,int ny) { if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3) return true; return false; } int copyDB(int (*a)[3],int (*b)[3]) //复制数组 { for(int i=0;i<3;i++) for(int j=0;j<3;j++) a[i][j] = b[i][j]; } void BFS() { queue<node> que; while(!que.empty()) que.pop(); int i,j,k; int nx,ny; node S; copyDB(S.puzzle,mp); copyDB(b,S.puzzle); int hsb = HS(b); vis[hsb] = 1; hash[hsb] = 0; S.step = 0; que.push(S); while(!que.empty()) { node tmp = que.front(); que.pop(); for(i=0;i<3;i++) for(j=0;j<3;j++) { b[i][j] = tmp.puzzle[i][j]; if(b[i][j] == 9) nx = i,ny = j; } for(k=0;k<4;k++) { int kx = nx + sx[k]; int ky = ny + sy[k]; if(!OK(kx,ky)) continue; swap(b[nx][ny],b[kx][ky]); int hsb = HS(b); //Cantor展开值作为状态 node now; if(!vis[hsb]) { copyDB(now.puzzle,b); vis[hsb] = 1; hash[hsb] = tmp.step + 1; now.step = tmp.step + 1; que.push(now); } swap(b[nx][ny],b[kx][ky]); } } } int main() { memset(hash,0,sizeof(hash)); memset(vis,0,sizeof(vis)); BFS(); while(gets(ss)) { int i,j,k = 0; for(i=0;i<3;i++) for(j=0;j<3;j++) { if(ss[k] == 'x') b[i][j] = 9; else b[i][j] = ss[k] - '0'; k += 2; } int hsb = HS(b); if(!hsb || (hsb && hash[hsb])) cout<<hash[hsb]<<endl; else puts("unsolvable"); } return 0; }
H.一个简单的走迷宫问题
因为走的是网格线,所以先把n*m的矩阵化成(n-1)*(m-1)的网格线矩阵,然后再来走。
状态的转移有6种状态:
1.向左转
2.向右转
3.向后转 (step = 2)
4.向此时的方向走1步
5.向此时的方向走2步
6.向此时的方向走3步
然后BFS就可以了。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define N 100007 int mp[55][55],nmp[55][55],flag[4],dis[4][55][55]; int R,C; struct node { int x,y; int dr; int step; bool operator<(const node &a)const { return step > a.step; } }S,E; bool OK(int nx,int ny) { if(nx >= 0 && nx < R-1 && ny >= 0 && ny < C-1) return true; return false; } int bfs(node S,node E) { priority_queue<node> que; while(!que.empty()) que.pop(); que.push(S); dis[S.dr][S.x][S.y] = 0; while(!que.empty()) { node tmp = que.top(); que.pop(); if(tmp.x == E.x && tmp.y == E.y) return tmp.step; memset(flag,0,sizeof(flag)); for(int i=2;i<=7;i++) //走的步数 { if(i <= 4) { node now; now.x = tmp.x; now.y = tmp.y; if(i == 2) //向右转然后走 { now.step = tmp.step + 1; now.dr = (tmp.dr + 1)%4; } else if(i == 3) //向左转然后走 { now.step = tmp.step + 1; now.dr = (tmp.dr + 3)%4; } else if(i == 4) //向后转然后走 { now.step = tmp.step + 2; now.dr = (tmp.dr + 2)%4; } if(dis[now.dr][now.x][now.y] != -1 && now.step >= dis[now.dr][now.x][now.y]) continue; dis[now.dr][now.x][now.y] = now.step; que.push(now); } else { node next; if(tmp.dr == 0) //UP { next.x = tmp.x - i + 4; next.y = tmp.y; } else if(tmp.dr == 1) //RIGHT { next.x = tmp.x; next.y = tmp.y + i - 4; } else if(tmp.dr == 2) //DOWN { next.x = tmp.x + i - 4; next.y = tmp.y; } else if(tmp.dr == 3) //LEFT { next.x = tmp.x; next.y = tmp.y - i + 4; } if(!OK(next.x,next.y) || nmp[next.x][next.y] == 1) break; next.step = tmp.step + 1; // next.dr = tmp.dr; if(dis[next.dr][next.x][next.y] != -1 && next.step >= dis[next.dr][next.x][next.y]) continue; dis[next.dr][next.x][next.y] = next.step; que.push(next); } } } return -1; } int main() { int i,j; while(scanf("%d%d",&R,&C)!=EOF && R && C) { memset(nmp,0,sizeof(nmp)); for(i=0;i<R;i++) { for(j=0;j<C;j++) { scanf("%d",&mp[i][j]); if(mp[i][j] == 1) { nmp[i][j] = 1; if(j>=1) { nmp[i][j-1] = 1; if(i>=1) nmp[i-1][j-1] = 1; } if(i>=1) { nmp[i-1][j] = 1; if(j>=1) nmp[i-1][j-1] = 1; } } } } memset(dis,-1,sizeof(dis)); scanf("%d%d%d%d%d",&S.x,&S.y,&E.x,&E.y,&S.dr); S.x--,S.y--,E.x--,E.y--; S.step = 0; E.dr = 0,E.step = Mod; int res = bfs(S,E); printf("%d\n",res); } return 0; }
I.分割包围
这题初看没思路,结果是个水二分搜索题。
二分最小长度,可以找到满足条件的最小长度的最大值。
如何判断满足条件呢?
即在给定的最小长度下,看至少需要操作多少下才能使这些兵团的两两间隔的最小长度为该长度,然后比较操作数与题目要求的最大操作数,如果小于,则可以,如果大于,则不满足,此时减小最小长度,再判断。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define Mod 1000000007 using namespace std; #define N 50007 int d[N]; int x,n,m; bool check(int mid) { int i,j; int cnt = 0; for(i=0;i<=n+1;i++) { for(j=i+1;j<=n+1;j++) { if(d[j]-d[i] < mid && j <= n) //如果两个间隔小于mid,则要增大,即抹除j的这个 cnt++; else if(d[j]-d[i] < mid && j == n+1) { cnt++; i = Mod; } else { i = j-1; break; } } } if(cnt <= m) return true; return false; } int main() { int i,j; scanf("%d%d%d",&x,&n,&m); for(i=1;i<=n;i++) scanf("%d",&d[i]); sort(d+1,d+n+1); d[0] = 0; d[n+1] = x; int mini = Mod; for(i=1;i<=n+1;i++) { if(d[i]-d[i-1] < mini) mini = d[i]-d[i-1]; } int low = mini; int high = x; while(low < high) { int mid = (low+high+1)/2; if(check(mid)) low = mid; else high = mid-1; } printf("%d\n",low); return 0; }
J.魔法少女小蟹
直接BFS肯定不行,复杂度太高。
先不考虑加加减操作,因为它们不涉及以为,很好处理。
因为一开始魔棒是在最左端,所以第i个位置被访问过了,则前面的一定都访问过。同时,我们可以直接通过和最后一位交换的方式访问最后一位。所以所有被访问的状态(标号)就只有1、12、123、1234、12345、123456、16、126、1236、12346,就10个。
所以状态记录就是dis[6][6][6][6][6][6][6][10],前6个6 代表现在的第i位是原来的第j位,第7个6 代表魔杖现在在哪,10 代表有哪些位置被访问过了。
dis表示到当前状态的步数(最小)。
BFS出来后,枚举6位数的每一位和指针位置和10种状态,看此状态是否已访问,如果已访问,表示能够到达这个状态,然后看从这个状态到目标状态还需多少步(有可能达不到),然后看dis[状态]+步数是否小于最小步数,是则更新答案。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #define Mod 1000000007 #define INT 2147483647 using namespace std; #define N 50007 struct node { int p[7]; int point,state; int step; }S; void Popy(int *p1,int *p2) { for(int i=1;i<7;i++) p1[i] = p2[i]; } int dis[7][7][7][7][7][7][7][10]; int E[7],SS[7]; int min(int ka,int kb) { if(ka < kb) return ka; return kb; } int max(int ka,int kb) { if(ka > kb) return ka; return kb; } void BFS(node S) { queue<node> que; memset(dis,-1,sizeof(dis)); que.push(S); dis[S.p[1]][S.p[2]][S.p[3]][S.p[4]][S.p[5]][S.p[6]][1][0] = 0; while(!que.empty()) { node tmp = que.front(); que.pop(); for(int i=0;i<4;i++) { node now; if(i == 0) //与左边交换 { swap(tmp.p[1],tmp.p[tmp.point]); Popy(now.p,tmp.p); swap(tmp.p[1],tmp.p[tmp.point]); now.point = tmp.point; now.state = tmp.state; now.step = tmp.step + 1; if(dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] == -1 || (dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] != -1 && now.step < dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state])) { dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] = now.step; que.push(now); } } else if(i == 1) //与右边交换 { swap(tmp.p[6],tmp.p[tmp.point]); Popy(now.p,tmp.p); swap(tmp.p[6],tmp.p[tmp.point]); now.point = tmp.point; if(tmp.state <= 3) now.state = tmp.state + 6; else if(tmp.state == 4) now.state = tmp.state + 1; else now.state = tmp.state; now.step = tmp.step + 1; if(dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] == -1 || (dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] != -1 && now.step < dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state])) { dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] = now.step; que.push(now); } } else if(i == 2) //左移 { Popy(now.p,tmp.p); now.point = max(1,tmp.point-1); now.state = tmp.state; now.step = tmp.step + 1; if(dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] == -1 || (dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] != -1 && now.step < dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state])) { dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] = now.step; que.push(now); } } else if(i == 3) //右移 { Popy(now.p,tmp.p); now.point = min(6,tmp.point+1); if(tmp.state < 5) now.state = tmp.state+1; else if(tmp.state == 5) now.state = tmp.state; else if(tmp.state < 9) now.state = tmp.state+1; else now.state = tmp.state-4; now.step = tmp.step + 1; if(dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] == -1 || (dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] != -1 && now.step < dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state])) { dis[now.p[1]][now.p[2]][now.p[3]][now.p[4]][now.p[5]][now.p[6]][now.point][now.state] = now.step; que.push(now); } } } } } int main() { int a,b,i; int h[7]; int pot,status; while(scanf("%d%d",&a,&b)!=EOF) { S.p[1] = 1; //标号。初始都在自己的位置 S.p[2] = 2; S.p[3] = 3; S.p[4] = 4; S.p[5] = 5; S.p[6] = 6; S.point = 1; S.state = 0; S.step = 0; SS[1] = (a/100000)%10; SS[2] = (a/10000)%10; SS[3] = (a/1000)%10; SS[4] = (a/100)%10; SS[5] = (a/10)%10; SS[6] = a%10; E[1] = (b/100000)%10; E[2] = (b/10000)%10; E[3] = (b/1000)%10; E[4] = (b/100)%10; E[5] = (b/10)%10; E[6] = b%10; BFS(S); int ans = Mod; for(h[1]=1;h[1]<=6;h[1]++) { for(h[2]=1;h[2]<=6;h[2]++) { if(h[2] != h[1]) { for(h[3]=1;h[3]<=6;h[3]++) { if(h[3] != h[2] && h[3] != h[1]) { for(h[4]=1;h[4]<=6;h[4]++) { if(h[4] != h[1] && h[4] != h[2] && h[4] != h[3]) { for(h[5]=1;h[5]<=6;h[5]++) { if(h[5] != h[1] && h[5] != h[2] && h[5] != h[3] && h[5] != h[4]) { for(h[6]=1;h[6]<=6;h[6]++) { if(h[6] != h[1] && h[6] != h[2] && h[6] != h[3] && h[6] != h[4] && h[6] != h[5]) { for(pot=1;pot<=6;pot++) { for(status=0;status<10;status++) { if(dis[h[1]][h[2]][h[3]][h[4]][h[5]][h[6]][pot][status] != -1) { int cnt = 0; int t,r; int flag = 1; //No. status if(status <= 5) //1 1 { //2 12 for(t=1;t<=status+1;t++) //3 123 cnt += abs(E[t]-SS[h[t]]); //4 1234 for(t;t<=6;t++) //5 12345 if(E[t] != SS[h[t]]) //6 123456 { //7 16 flag = 0; //8 126 break; //9 1236 } //10 12346 if(!flag) continue; } else if(status >= 6 && status <= 9) { for(r=1;r<=status-5;r++) cnt += abs(E[r]-SS[h[r]]); for(r;r<6;r++) if(E[r] != SS[h[r]]) { flag = 0; break; } if(!flag) continue; cnt += abs(E[6]-SS[h[6]]); } ans = min(ans,dis[h[1]][h[2]][h[3]][h[4]][h[5]][h[6]][pot][status]+cnt); } } } } } } } } } } } } } } printf("%d\n",ans); } return 0; }
K.Can You Help God Wu
BFS,枚举涂法,用每个位置用0表示染上了错误的颜色,1表示染上了正确的颜色,用一个十进制数表示,最大不超过2^16.
开始的时候暴力枚举每一个矩形,每一种颜色,然后涂色,但是发现复杂度太高。TLE了。
然后改变做法,每次选一个位置的颜色进行扩展,使尽可能大的区域染上这个颜色。并且每次要将该状态的子集(1到该状态的值)也加入队列中,因为最多两行,所以考虑一行扩展和两行(一块)扩展,每次往左往右扩展染色,如果已经染好了,就跳出,表示那边已经不能再染了。直到找到目标状态(二进制位全为1)。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define N 250007 struct node { int num,step; }S; char E[3][18]; int vis[70000]; int n,C; int BFS(node S) { queue<node> que; que.push(S); vis[S.num] = 1; int D = ((1<<(2*n))-1); while(!que.empty()) { node tmp = que.front(); que.pop(); if(tmp.num == D) return tmp.step; int i,j,a,b; for(i=0;i<2;i++) { for(j=0;j<n;j++) { int pos = i*n+j; node now; if(tmp.num & (1<<pos)) //已经是该颜色 continue; int Num = 0; for(a=pos;a<(i==0?n:2*n);a++) //往右 { if(tmp.num & (1<<a)) //已经染好,不能再涂 break; if(E[a/n][a%n] == E[pos/n][pos%n]) Num = Num | (1<<a); //染色 } for(a=pos-1;a>=(i==0?0:n);a--) //往左 { if(tmp.num & (1<<a)) break; if(E[a/n][a%n] == E[pos/n][pos%n]) Num = Num | (1<<a); } for(pos=Num;pos!=0;pos=Num&(pos-1)) //子状态 { if(vis[tmp.num|pos]) break; vis[tmp.num|pos] = 1; now.num = tmp.num|pos; now.step = tmp.step + 1; que.push(now); } pos = i*n+j; if(i >= 1) continue; if(tmp.num & (1<<(n+pos))) continue; Num = 0; for(a=pos;a<n;a++) //双行往右 { if(tmp.num & (1<<a) || tmp.num & (1<<(a+n))) break; if(E[a/n][a%n] == E[pos/n][pos%n]) Num = Num | (1<<a); if(E[(a+n)/n][(a+n)%n] == E[pos/n][pos%n]) Num = Num | (1<<(a+n)); } for(a=pos-1;a>=0;a--) //双行往左 { if(tmp.num & (1<<a) || tmp.num & (1<<(a+n))) break; if(E[a/n][a%n] == E[pos/n][pos%n]) Num = Num | (1<<a); if(E[(a+n)/n][(a+n)%n] == E[pos/n][pos%n]) Num = Num | (1<<(a+n)); } for(pos=Num;pos!=0;pos=Num&(pos-1)) //子状态 { if(vis[tmp.num|pos]) continue; vis[tmp.num|pos] = 1; now.num = tmp.num|pos; now.step = tmp.step + 1; que.push(now); } } } } } int main() { int t,cs = 1,i,j; scanf("%d",&t); while(t--) { scanf("%d",&n); for(i=0;i<=1;i++) scanf("%s",E[i]); memset(vis,0,sizeof(vis)); S.num = 0; S.step = 0; int res = BFS(S); printf("Case #%d: %d\n",cs++,res); } return 0; }
L.STAMPS
DFS
参数为sum:当前组合的和 和 step:当前已经用到了第几种邮票,邮票可以重复使用。限制最大搜索深度为4
如果找到答案,则根据一系列规则比较该方案是否优于答案,是则更新。出现tie则isTie置为True。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> using namespace std; #define N 1007 struct node { int deno[5]; int num; }E,tmp; int stamp[N]; int vis[N]; int n,m,S; int isTie,flag; int Totaltype(node ka) //计算这个组合的种类数 { memset(vis,0,sizeof(vis)); int res = 0; for(int i=0;i<ka.num;i++) if(!vis[ka.deno[i]]) res++,vis[ka.deno[i]] = 1; return res; } int Max(node ka) //找出票面价值的最大值 { int maxi = -Mod; for(int i=0;i<ka.num;i++) if(maxi < stamp[ka.deno[i]]) maxi = stamp[ka.deno[i]]; return maxi; } int copyAN(int *a,int *b,int num) //复制结果 { for(int i=0;i<num;i++) a[i] = b[i]; } void check(node &ka,node &kb) //比较函数 { int typa = Totaltype(ka); int typb = Totaltype(kb); int maxa = Max(ka); int maxb = Max(kb); if(ka.num == -Mod || (typa < typb) || (typa == typb && ka.num > kb.num) || (typa == typb && ka.num == kb.num && maxa < maxb)) { copyAN(ka.deno,kb.deno,kb.num); ka.num = kb.num; isTie = 0; return; } if(typa == typb && ka.num == kb.num && maxa == maxb) isTie = 1; } void DFS(int sum,int step) //sum:当前组合的和 step:当前已经用到了第几种邮票 { if(sum == S) { flag = 1; check(E,tmp); return; } if(sum > S || tmp.num >= 4) return; for(int k=step;k<n;k++) //可用多次,所以从上次结束的位置开始枚举 { tmp.deno[tmp.num] = k; tmp.num++; DFS(sum+stamp[k],k); tmp.num--; } } int main() { int x,y,i,j; while(scanf("%d",&x)!=EOF && x) { n = 0; stamp[0] = x; while(stamp[n]) { n++; scanf("%d",&stamp[n]); } //stamp[n] = 0 sort(stamp,stamp+n); while(scanf("%d",&S)!=EOF && S) { printf("%d ",S); memset(E.deno,0,sizeof(E.deno)); E.num = -Mod; memset(tmp.deno,0,sizeof(tmp.deno)); tmp.num = 0; flag = isTie = 0; DFS(0,0); if(!flag) puts("---- none"); else { sort(E.deno,E.deno+E.num); printf("(%d):",Totaltype(E)); if(isTie) puts(" tie"); else { for(i=0;i<E.num;i++) printf(" %d",stamp[E.deno[i]]); printf("\n"); } } } } return 0; }
(2014.5.17 0:34)