/*题意:这个题目的意思是给定你起点S,和终点D,问你是否能在 T 时刻恰好到达终点D。分析:这样一看很明显是DFS,不过里面涉及到很多剪枝。 奇偶剪枝:是数据结构的搜索中,剪枝的一种特殊小技巧。现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点, s | | | +———e 如图所示(“|”竖走,“—”横走,“+”转弯),易证abs(ex-sx)+abs(ey-sy)为此问题类中任意情况下,起点到终点的最短步数,记做step,此处step1=8; s——— ——+ |+ | +———e 如图,为一般情况下非最短路径的任意走法举例,step2=14;step2-step1=6,偏移路径为6,偶数(易证);故,若t-[abs(ex-sx)+abs(ey-sy)]结果为非偶数(奇数),则无法在t步恰好到达;返回,false;反之亦反。 心得:起初没仔细看题,以为是 T 时间内到达的,果断 BFS 呀,于是果断 WA ,于是就把题目在仔细看了一遍,没想到是 T 时刻到达,所以心凉了一截,再次果断 DFS ,稍微剪下枝,就交了TLE,无语了,想了好久没不知道怎么剪枝的,后来看了下解题报告才知道还有个奇偶剪枝,挺强大的。以后要仔细看题*/ #include<iostream> #include<cstring> #define N 10 using namespace std; int n,m,t,end_i,end_j; bool visited[N][N],flag,ans; char map[N][N]; int abs(int a,int b) { if(a<b) return b-a; else return a-b; } void DFS(int i,int j,int c) { if(flag) return ; if(c>t) return ; if(i<0||i>=n||j<0||j>=m) {return ;} if(map[i][j]=='D'&&c==t) {flag=ans=true; return ;} int temp=abs(i-end_i)+abs(j-end_j); temp=t-temp-c; if(temp&1) return ;//奇偶剪枝 if(!visited[i-1][j]&&map[i-1][j]!='X') { visited[i-1][j]=true; DFS(i-1,j,c+1); visited[i-1][j]=false; } if(!visited[i+1][j]&&map[i+1][j]!='X') { visited[i+1][j]=true; DFS(i+1,j,c+1); visited[i+1][j]=false; } if(!visited[i][j-1]&&map[i][j-1]!='X') { visited[i][j-1]=true; DFS(i,j-1,c+1); visited[i][j-1]=false; } if(!visited[i][j+1]&&map[i][j+1]!='X') { visited[i][j+1]=true; DFS(i,j+1,c+1); visited[i][j+1]=false; } } int main() { int i,j,x,y,k; while(cin>>m>>n>>t&&(m||n||t)) { memset(visited,false,sizeof(visited)); k=0; for(i=0;i<n;i++) { for(j=0;j<m;j++) { cin>>map[i][j]; if(map[i][j]=='S') { x=i;y=j; visited[i][j]=true; } if(map[i][j]=='D') { end_i=i;end_j=j; } if(map[i][j]=='X')k++; } } ans=flag=false; if(n*m-k-1>=t) DFS(x,y,0); if(ans) cout<<"YES"<<endl; else cout<<"NO"<<endl; } return 0; }
Prime Ring Problem
#include<iostream> #define N 25 #define M 40 using namespace std; bool is_prime[M],visited[N]; int n,test,ans[N]; void work(int k) { int i; if(k==n+1) { if(!is_prime[ans[n]+ans[1]]) return ; for(i=1;i<=n-1;i++) cout<<ans[i]<<" "; cout<<ans[i]<<endl; return ; } for(i=2;i<=n;i++) { if(!visited[i]&&is_prime[ans[k-1]+i]) { visited[i]=true; ans[k]=i; work(k+1); visited[i]=false; } } } bool prime(int n) { if(n==1) return false; if(n==2||n==3) return true; int i; for(i=2;i<n;i++) if(n%i==0) return false; return true; } int main() { int i;test=1; for(i=1;i<M;i++) is_prime[i]=prime(i); while(cin>>n) { ans[1]=1; memset(visited,false,sizeof(visited)); cout<<"Case "<<test<<":"<<endl; work(2); test++; cout<<endl; } return 0; }
蜘蛛牌
#include <iostream> #include <queue> using namespace std; struct Node{ int min; int max; }; int num[11]; int used[11]; int ans; void DFS(int rank,int sum){ if(sum > ans){//剪枝 return; } if(rank == 9){//递归出口 ans = sum; return; } int i,j; for(i=1;i<=10;i++){ if(!used[i]){//枚举每一种可能 for(j=i+1;j<=10;j++){//这个for判断枚举的可能性是否合法 if(!used[j]){//判断1时,发现2,3已被使用说明2,3都被放在了4的位置上 used[i] = 1; DFS(rank+1,sum+abs(num[i]-num[j])); break; } } used[i] = 0; } } } int main(){ int T; cin>>T; while(T--){ int i; for(i=1;i<=10;++i){ int x; cin>>x; num[x] = i;/*用下标表示每个数字,数值记录位置 数字则变成升序的排列 在堆叠时若发现右边已经被使用则所要的卡牌必然再更后面*/ } ans = 0x3fffffff; DFS(0,0); cout<<ans<<endl; } return 0; }
Remainder
#include<stdio.h>#include<string.h>#define N 1000001/*简单数论+bfs的题WA了一上午,关键在于要对k*m取模来保存状态信息。*/struct node{ int r; int pre; char op;}q[N];int n,k,m,vis[N],km;int mode(int t,int i){ switch (i){ case 0: return ((t+m)%km+km)%km; case 1: return ((t-m)%km+km)%km; case 2: return ((t*m)%km+km)%km; case 3: return (t%m+m)%m%km; }}char f[5]="+-*%";int bfs(){ memset(vis,0,sizeof(vis)); int i,t,p,pre,front,rear,goal; goal=((n+1)%k+k)%k; front=rear=0; t=n; // if(n==goal)//这里不考虑初始就相等也可以AC // return -1; for(i=0;i<4;++i) { p=mode(t,i); vis[p]=1; q[rear].op=f[i]; q[rear].r=p; q[rear].pre=-1; p=p%k; if(p==goal) return rear; rear++; } while(front<rear) { pre=front; t=q[front++].r; for(i=0;i<4;++i) { p=mode(t,i); if(!vis[p]) { vis[p]=1; q[rear].op=f[i]; q[rear].r=p; q[rear].pre=pre; p%=k; if(p==goal) return rear; rear++; } } } return -1;}void solve(){ int s=bfs(); if(s<0) { printf("0\n"); return ; } int i=0; char str[N]; while(s!=-1) { str[i++]=q[s].op; s=q[s].pre; } printf("%d\n",i); --i; while(i>0) printf("%c",str[i--]); printf("%c\n",str[i]);}int main(){ while(scanf("%d%d%d",&n,&k,&m)==3&&(n||k||m)) { km=k*m; solve(); } return 0;}
Sudoku Killer
#include<stdio.h>int map[9][9],kong[90][2],kn;int ci=0,f;int he(int i,int j,int k)//判断能填k不能{int o,p,oo,pp;for(o=0;o<9;o++)if(map[i][o]==k)return 0;for(o=0;o<9;o++)if(map[o][j]==k)return 0;oo=i/3*3;pp=j/3*3;for(o=oo;o<(oo+3);o++)for(p=pp;p<(pp+3);p++)if(map[o][p]==k)return 0;return 1;}void shu()//输出{int i,j;f=1;if(ci!=1)printf("\n");for(i=0;i<9;i++){printf("%d",map[i][0]);for(j=1;j<9;j++)printf(" %d",map[i][j]);printf("\n");}}void dfs(int l){int k;if(l<kn){for(k=1;k<10;k++)//依次试试kong[l]该赋何值{if(he(kong[l][0],kong[l][1],k)){map[kong[l][0]][kong[l][1]]=k;dfs(l+1);if(f) return;map[kong[l][0]][kong[l][1]]=0;}}}elseshu();}int main(){char c;int i,j;while(1){kn=0;for(i=0;i<9;i++)for(j=0;j<9;j++){scanf("%c",&c);if(c=='?'){map[i][j]=0;kong[kn][0]=i;//把是?的坐标记录到kong[]中,以便后面依次赋值kong[kn][1]=j;kn++;}else map[i][j]=c-'0';getchar();}ci++;f=0;dfs(0);//从kong[0]开始赋值i=scanf("%c",&c);if(i==-1)//若不能读到字符,跳出break;}return 0;}
N皇后问题
/**n皇后问题,由于N 是小于等于10的正整数,所以可以使用打表的方法把前十中情况全部找出来存起来然后每次输入时不用重新的计算了。*/#include <stdio.h>#define NUMS 10/*输入的数字1---10*/int N;/*棋盘*/int chessboard[11][11];/* 用来记录拜访数目 */int cal;/*检查皇后放置此行此列是否可以,可以返回1,不可以返回0此递归是一行一行找的,K是棋盘的长度*/int dfs_check(int row,int column,int k){ /* 说明已经到了棋盘的界外,前边都符合了 */ if(row>k) { cal++; return 1; } /* 正上方是否有皇后*/ for(int i = 1; i < row; i++) /* 如果有皇后则返回不能放置这里返回0*/ if(chessboard[i][column] == 1) return 0; /* 左右上方45度角检查是否可以*/ /* 左上方*/ for(int i=row-1,j=column-1;i>0&&j>0;i--,j--) if(chessboard[i][j] == 1) return 0; /* 右上方*/ for(int i=row-1,j=column+1;i>0&&j<=k;i--,j++) if(chessboard[i][j] == 1) return 0; /*标记这个位置成功了*/ chessboard[row][column] = 1; /*进行下一行搜索*/ for(int i=1;i<=k;i++) if(dfs_check(row+1,i,k)==1) break; chessboard[row][column] = 0; return 0;}int main(){ int i,j,k; int count[11]; /*打表*/ for(k=1;k<=NUMS;k++) { count[k] = 0; cal = 0; /*首先将棋盘初始化全部置为0*/ for(i=0;i<=NUMS;i++) for(j=0;j<=NUMS;j++) chessboard[i][j]=0; for(i=1;i<=k;i++) dfs_check(1,i,k); count[k] = cal; } while(scanf("%d",&N)!=EOF&&N!=0) printf("%d\n",count[N]); return 0;}
The magic apple tree
#include <iostream>#include <algorithm>#include <cstdio>#include <vector>#include <cstring>using namespace std;const int maxn=20001;vector<int> g[maxn];int in[maxn], out[maxn];int dfs(int root){ if(out[root]==0) return root; vector<int> tmp; for(int i=0;i<out[root];i++) tmp.push_back(dfs(g[root][i])); sort(tmp.begin(),tmp.end()); return tmp[(out[root]+1)/2-1];}char c;inline void scan(int &x){ while(c=getchar(),c<'0'||c>'9'); x=c-'0'; while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';}int main(){ int N,tmp,i,j,root; while(~scanf("%d",&N)) { for(i=1;i<=N;i++) g[i].clear(); memset(in,0,sizeof(in)); for(i=1;i<=N;i++) { scan(out[i]); for(j=1;j<=out[i];j++) { scan(tmp); g[i].push_back(tmp); in[tmp]++; } } for(i=1;i<=N;i++) { if(in[i]==0) { root=i; break; } } printf("%d\n",dfs(root)); } return 0;}
Fling
/*这题题意是一个弹来弹去的游戏。在一个7*8的板子上,有若干个球(小于12个,经测试最多11个)。你每次可以选择一个球向上下左右推动,能推动的条件是推动的方向上有球但是不能粘在一起,中间必需得隔一个及以上的格子。然后你推动这个球后它会一直在这个方向上滚动,直到碰到下一个球或者掉下板子去。如果碰到下一个球他的动能会传递下去,如果碰到的球紧挨着另一个球就隔山打牛,而原来的球就停在碰到的球的前一个位置上。然后结束标志是板子上只剩一个球。输出每次操作的球的坐标和推动的方向(ULRD)。如果有多种情况就按照越左上角越好和U,L,R,D的优先级输出优先级最高的情况。起初是想记录下所有的O点保存进结构体,这样就一定满足左上角在前来进行搜索了。然后把ULRD编作1234进行分类讨论,当搜到满足条件的情况时输出的自然就是优先级最高的解了。实际上这样也是可行的,但是中途卡了一段时间导致我重新换了思路。最后我是每次DFS都把地图搜一遍先搜到的先进行操作,结果是一样的。但是可能会慢点。思路是很简单,但这题烦在回溯和Debug上………………*/#include <cstdio>#define MAX 8using namespace std;char map[7][MAX];char s[11][MAX];int n,flag,total;struct node{ int i,j; int run;}road[11];bool judge(int curi,int curj,int x) //判断是否可推函数,长了Debug好痛苦。。。{ int i; if(x==1){ if(curi-1<0||(map[curi-1][curj]=='O')) return 0; for(i=2;curi-i>=0;i++) if(map[curi-i][curj]=='O') return 1; }else if(x==2){ if(curj-1<0||(map[curi][curj-1]=='O')) return 0; for(i=2;curj-i>=0;i++) if(map[curi][curj-i]=='O') return 1; }else if(x==3){ if(curj+1>=8||(map[curi][curj+1]=='O')) return 0; for(i=2;curj+i<8;i++) if(map[curi][curj+i]=='O') return 1; }else if(x==4){ if(curi+1>=7||(map[curi+1][curj]=='O')) return 0; for(i=2;curi+i<7;i++) if(map[curi+i][curj]=='O') return 1; } return 0;}void dfs(){ int i,j,k,l,l2; if(total==n){ for(i=1;i<total;i++){ printf("%d %d ",road[i].i,road[i].j); if(road[i].run==1) printf("U\n"); else if(road[i].run==2) printf("L\n"); else if(road[i].run==3) printf("R\n"); else if(road[i].run==4) printf("D\n"); } flag=1; return ; } for(i=0;i<7;i++){ for(j=0;j<8;j++){ if(map[i][j]=='O'){ for(k=1;k<=4;k++){ if(!judge(i,j,k))continue; road[total].i=i; road[total].j=j; road[total].run=k; if(k==1||k==4){ for(l=0;l<7;l++){ s[total][l]=map[l][j]; //该列改变了,记录该列。 } } else{ for(l=0;l<8;l++){ s[total][l]=map[i][l]; //该行改变了,记录该行。 } } map[i][j]='X'; if(k==1){ //分类讨论推动方向 l2=i; for(l=i-1;l>=0;l--){ if(map[l][j]=='O'){ map[l+1][j]='O'; if(l2!=l+1){ map[l2][j]='X'; } l2=l; } } map[l2][j]='X'; }else if(k==2){ l2=j; for(l=j-1;l>=0;l--){ if(map[i][l]=='O'){ map[i][l+1]='O'; if(l2!=l+1){ map[i][l2]='X'; } l2=l; } } map[i][l2]='X'; }else if(k==3){ l2=j; for(l=j+1;l<8;l++){ if(map[i][l]=='O'){ map[i][l-1]='O'; if(l-1!=l2){ map[i][l2]='X'; } l2=l; } } map[i][l2]='X'; }else if(k==4){ l2=i; for(l=i+1;l<7;l++){ if(map[l][j]=='O'){ map[l-1][j]='O'; if(l-1!=l2){ map[l2][j]='X'; } l2=l; } } map[l2][j]='X'; } total++; dfs(); if(flag)return ; total--; if(k==1||k==4){ //进行回溯。 for(l=0;l<7;l++){ map[l][j]=s[total][l]; } } else{ for(l=0;l<8;l++){ map[i][l]=s[total][l]; } } } } } }}int main(){ int casi=0; int i,j; while(scanf("%s",s[0])!=EOF){ if(casi)printf("\n"); n=0; for(i=0;i<8;i++){ map[0][i]=s[0][i]; if(map[0][i]=='O') n++; } for(i=1;i<7;i++){ scanf("%s",s[0]); for(j=0;j<8;j++){ map[i][j]=s[0][j]; if(map[i][j]=='O') n++; } } flag=0; total=1; printf("CASE #%d:\n",++casi); dfs(); } return 0;}