深度优先搜索DFS与回溯
导入:数独问题 深入浅出程序设计竞赛187页
学生基础:必须在熟练掌握递归和暴力枚举的基础上
需要讲解:函数栈空间
P1706 全排列问题
#include<iostream> using namespace std; int n; int v[10];//标记i有没被选中 int a[10];//记录最终的序列 void dfs(int k) { //层级,序列中已经有了几个数字 if(k>n){//k==n+1 for(int i=1;i<=n;i++){ printf("%5d",a[i]); } printf("\n"); return; } for(int i=1;i<=n;i++){ if(!v[i]){//没有被选中的数字 v[i]=1; a[k]=i; dfs(k+1); v[i]=0;//复原 a[k]=0;//可以省略 } } return; } int main() { scanf("%d",&n); dfs(1); return 0; }
STL:next_permutation
1 #include<iostream> 2 #include<algorithm>//使用 next_permutation()和sort()需要导入的头文件 3 using namespace std; 4 int main(){ 5 int a[4]={2,1,4,3}; 6 7 sort(a,a+4);//对数组排序 8 9 do{ 10 for(int i=0;i<4;i++){//打印排列 11 cout<<a[i]<<' '; 12 } 13 cout<<endl; 14 }while(next_permutation(a,a+4));//获取下一个排列 15 }
子集生成《算法竞赛入门经典》188页
需要大量做题以后才能够总结、掌握、理解,新课讲授时候可暂时略过
增量构造法
位向量法
二进制法(需要熟练掌握位运算)
P1219. [USACO1.5] 八皇后 Checker Challenge
1 #include<iostream> 2 using namespace std; 3 int n; 4 int tot=0; 5 int a[15]; 6 void search(int k) { 7 if(k>n) { 8 tot++;//递归的边界,走到这一步必然符合条件 9 if(tot<4) { 10 for(int i=1; i<=n; i++) { 11 cout<<a[i]<<' '; 12 } 13 cout<<endl; 14 } 15 return; 16 } 17 for(int i=1; i<=n; i++) { 18 int ok=1; 19 a[k]=i;//把第k行的皇后放在i列 20 int ux=k; 21 int uy=i; 22 for(int j=1; j<k; j++) { 23 int vx=j; 24 int vy=a[j]; 25 if(uy==vy||ux+uy==vx+vy||ux-uy==vx-vy) {//列冲突,左、右对角线冲突 26 ok=0; 27 break; 28 } 29 } 30 if(ok) search(k+1); 31 } 32 return; 33 } 34 int main() { 35 cin>>n; 36 search(1); 37 cout<<tot; 38 return 0; 39 }
1 #include<iostream> 2 using namespace std; 3 int n; 4 int tot=0; 5 int a[15]; 6 int vy[30],vl[30],vr[30]; 7 void search(int k) { 8 if(k>n) { 9 tot++;//递归的边界,走到这一步必然符合条件 10 if(tot<4) { 11 for(int i=1; i<=n; i++) { 12 cout<<a[i]<<' '; 13 } 14 cout<<endl; 15 } 16 return; 17 } 18 for(int i=1; i<=n; i++) { 19 int x=k; 20 int y=i; 21 if(!vy[y]&&!vl[x+y]&&!vr[x-y+n]) { 22 a[k]=i; 23 vy[y]=1; 24 vl[x+y]=1; 25 vr[x-y+n]=1; 26 search(k+1); 27 vy[y]=0; 28 vl[x+y]=0; 29 vr[x-y+n]=0; 30 } 31 } 32 return; 33 } 34 int main() { 35 cin>>n; 36 search(1); 37 cout<<tot; 38 return 0; 39 }
素数环 Prime Ring Problem
https://www.luogu.com.cn/problem/UVA524
#include<iostream> #include<cstring> using namespace std; int n,tot; int a[20],v[20]; void print() { for(int i=1; i<n; i++) { cout<<a[i]<<' '; } cout<<a[n]<<endl; } bool is_prime(int x) { if(x<=1) return false; for(int i=2; i*i<=x; i++) { if(x%i==0) return false; } return true; } void prime_ring(int k) { if(k>n) { if(is_prime(a[1]+a[n])) print(); return; } for(int i=1; i<=n; i++) { if(!v[i]&&(k==1||is_prime(a[k-1]+i))) { v[i]=1; a[k]=i; prime_ring(k+1); v[i]=0; } } return; } int main() { a[1]=1; v[1]=1; while(cin>>n) { if(tot>=1) cout<<endl; tot++; cout<<"Case "<<tot<<':'<<endl; prime_ring(2); } return 0; }
P2404 自然数的拆分问题
1 #include<iostream> 2 #include<cstdio> 3 #include<iomanip> 4 using namespace std; 5 int n; 6 int a[10]; 7 void print(int k) { 8 printf("%d",a[1]); 9 for(int i=2; i<k; i++) { 10 printf("+%d",a[i]); 11 } 12 printf("\n"); 13 } 14 15 void work(int x,int k) { 16 if(x==0) { 17 if(k!=2) print(k); 18 return; 19 } 20 for(int i=a[k-1]; i<=x; i++) { 21 x-=i; 22 a[k]=i; 23 work(x,k+1); 24 x+=i; 25 } 26 return; 27 } 28 int main() { 29 cin>>n; 30 a[0]=1; 31 work(n,1); 32 return 0; 33 }
P2392 kkksc03考前临时抱佛脚
深入浅出程序设计竞赛192
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 int s[5][25]; 6 int tot=0,t1=1,t2=0; 7 int mt=60*20+10; 8 void search(int x,int k) { 9 if(k>s[x][0]) { 10 // cout<<"t1="<<t1<<" t2="<<t2<<endl; 11 mt=min(max(t1,t2),mt); 12 return; 13 } 14 t1+=s[x][k]; 15 search(x,k+1); 16 t1-=s[x][k]; 17 18 t2+=s[x][k]; 19 search(x,k+1); 20 t2-=s[x][k]; 21 return; 22 } 23 24 int main() { 25 for(int i=1; i<=4; i++) cin>>s[i][0]; 26 for(int i=1; i<=4; i++) { 27 for(int j=1; j<=s[i][0]; j++) { 28 cin>>s[i][j]; 29 } 30 } 31 for(int i=1; i<=4; i++) { 32 t1=0; 33 t2=0; 34 mt=60*20+10; 35 search(i,1); 36 //cout<<mt<<endl; 37 tot+=mt; 38 } 39 cout<<tot; 40 return 0; 41 }
优化方向:选择了一些题目,但是这些题目的总耗时超过了所有题目总耗时的一半,那这些题目的集合就不合法,没有继续搜索下去的必要了。
#include<iostream> #include<cstdio> #include<cmath> using namespace std; int s[5][25],sum[5]; int tot=0,t1=1; int mt=60*20+10; void search(int x,int k) { if(k>s[x][0]) { // cout<<"t1="<<t1<<" t2="<<t2<<endl; mt=min(max(t1,sum[x]-t1),mt); return; } //选 if(t1+s[x][k]<=sum[x]/2) { t1+=s[x][k]; search(x,k+1); t1-=s[x][k]; } //不选 search(x,k+1); return; } int main() { for(int i=1; i<=4; i++) cin>>s[i][0]; for(int i=1; i<=4; i++) { for(int j=1; j<=s[i][0]; j++) { cin>>s[i][j]; sum[i]+=s[i][j]; } } for(int i=1; i<=4; i++) { t1=0; mt=60*20+10; search(i,1); //cout<<mt<<endl; tot+=mt; } cout<<tot; return 0; }
其他题目:
C000004. 马走日
1 #include<iostream> 2 #include<cstdio> 3 #include<iomanip> 4 #include<cstring> 5 using namespace std; 6 int t,n,m,v[15][15]; 7 int x,y,tot; 8 int dir[8][2]= {{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}}; 9 bool in_map(int x,int y) { 10 if(x >= 0 && x < n && y >= 0 && y < m) return true; 11 else return false; 12 } 13 14 void search(int x,int y,int k) { 15 if(k==0) { 16 tot++; 17 return; 18 } 19 for(int i=0; i<8; i++) { 20 //int nx=x+dx[i]; 21 //int ny=y+dy[i]; 22 int nx = x + dir[i][0], ny = y + dir[i][1]; 23 if(in_map(nx,ny)&&!v[nx][ny]) { 24 v[nx][ny]=1; 25 search(nx,ny,k-1); 26 v[nx][ny]=0; 27 // cout<<nx<<ny<<endl; 28 } 29 } 30 return; 31 } 32 33 int main() { 34 cin>>t; 35 while(t--) { 36 cin>>n>>m>>x>>y; 37 tot=0; 38 memset(v,0,sizeof v); 39 v[x][y]=1; 40 search(x,y,n*m-1); 41 cout<<tot<<endl; 42 } 43 return 0; 44 }
P2386. Lake Counting
挑战程序设计竞赛33页
从任意的W开始,不停地把邻接的部分用.代替。1次DFS以后与初始的这个W连接的所有W就都被替换成了. 。因此直到图中不再存在W为止,总共进行的DFS的次数就是答案了。
8个方向总共对应了8种转移状态,每个格子作为DFS的参数至多被调用一次,所以复杂度为O(8*n*m).
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 int n,m,ans=0; 6 int a[110][110]; 7 int dx[10]= {0,-1,-1,-1,0,0,1,1,1}; 8 int dy[10]= {0,-1,0,1,-1,1,-1,0,1}; 9 void dfs(int x,int y) { 10 for(int i=1; i<=8; i++) { 11 int nx=x+dx[i]; 12 int ny=y+dy[i]; 13 if(a[nx][ny]==1) { 14 a[nx][ny]=0; 15 dfs(nx,ny); 16 } 17 } 18 return; 19 } 20 21 int main() { 22 cin>>n>>m; 23 for(int i=1; i<=n; i++) { 24 for(int j=1; j<=m; j++) { 25 char c; 26 cin>>c; 27 if(c=='.') a[i][j]=0;//旱地 28 else a[i][j]=1;//水坑 29 } 30 } 31 for(int i=1; i<=n; i++) { 32 for(int j=1; j<=m; j++) { 33 if(a[i][j]==1) { 34 ans++; 35 dfs(i,j); 36 } 37 } 38 } 39 cout<<ans; 40 return 0; 41 }
困难的串 Krypton Factor
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 int n,l,tot=1; 6 string s; 7 bool ok(string x) { 8 int xl=x.length(); 9 for(int i=1; i<=xl/2; i++) { 10 int sl=xl-2*i; 11 int sr=xl-i; 12 int flag=1;//默认是容易的串 13 //cout<<"i="<<i<<" sl="<<sl<<" sr="<<sr<<endl; 14 for(int j=0; j<i; j++) { 15 if(x[sl+j]!=x[sr+j]) { 16 flag=0; 17 break; 18 } 19 } 20 if(flag==1) return false; 21 } 22 return true; 23 } 24 25 void print(string t) { 26 int len=t.length(); 27 for(int i=1; i<=len; i++) { 28 printf("%c",t[i-1]); 29 if(i%4==0&&i%64!=0&&i!=len ) cout<<" "; 30 if(i%64==0&&i!=len) cout<<endl; 31 } 32 printf("\n"); 33 printf("%d\n",len); 34 } 35 void dfs(string c) { 36 if(tot>n) return; 37 for(int i=0; i<l; i++) { 38 string t=c+char(i+'A'); 39 // cout<<"try "<<t<<endl; 40 if(ok(t)) { //困难的串 41 if(tot==n) { 42 print(t); 43 } 44 tot++; 45 dfs(t); 46 } 47 } 48 return; 49 } 50 51 int main() { 52 while(1) { 53 cin>>n>>l; 54 tot=1; 55 s=""; 56 if(n==0&&l==0) break; 57 dfs(s); 58 // cout<<ok("ABAC"); 59 } 60 return 0; 61 }
P1123 取数游戏
1 //P1123 取数游戏 2 #include<iostream> 3 #include<queue> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 const int N=10; 8 int t,n,m,ans; 9 int g[N][N],vis[N][N]; 10 int dx[]= {-1,-1,-1,0,0,1,1,1}; 11 int dy[]= {-1,0,1,-1,1,-1,0,1}; 12 void dfs(int x,int y,int sum) { 13 if(y>m) {//换行 14 x++; 15 y=1; 16 } 17 if(x==n+1) {//出口 18 ans=max(ans,sum); 19 return; 20 } 21 //不选 22 dfs(x,y+1,sum); 23 //选 24 int f=1; 25 for(int i=0; i<8; i++) { 26 int tx=x+dx[i]; 27 int ty=y+dy[i]; 28 if(vis[tx][ty]==1) { 29 f=0; 30 break; 31 } 32 } 33 if(f) { //格子可以选 34 vis[x][y]=1; 35 dfs(x,y+1,sum+g[x][y]); 36 vis[x][y]=0; 37 } 38 return; 39 } 40 41 int main() { 42 cin>>t; 43 while(t--) { 44 cin>>n>>m; 45 for(int i=1; i<=n; i++) { 46 for(int j=1; j<=m; j++) { 47 cin>>g[i][j]; 48 } 49 } 50 memset(vis,0,sizeof vis); 51 ans=0; 52 dfs(1,1,0);//第一个格子 53 cout<<ans<<endl; 54 } 55 return 0; 56 }
P2196 [NOIP1996 提高组] 挖地雷
1 //P2196 [NOIP1996 提高组] 挖地雷 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 const int N=15; 7 int n; 8 int g[N][N],a[N]; 9 int ans=0,c[N],num=0; 10 int b[N],vis[N]; 11 12 void dfs(int x,int idx,int sum) { 13 if(sum>ans) { 14 ans=sum; 15 num=idx; 16 for(int i=0; i<idx; i++) { 17 c[i]=b[i]; 18 } 19 } 20 for(int i=1; i<=n; i++) { 21 if(vis[i]==0&&g[x][i]==1) { 22 vis[i]=1; 23 b[idx]=i; 24 dfs(i,idx+1,sum+a[i]); 25 vis[i]=0; 26 b[idx]=0; 27 } 28 } 29 return; 30 } 31 32 int main() { 33 cin>>n; 34 for(int i=1; i<=n; i++) cin>>a[i];//每个地窖中的地雷 35 for(int i=1; i<=n; i++) { 36 for(int j=i+1; j<=n; j++) { 37 int b; 38 cin>>b; 39 g[i][j]=b; 40 } 41 } 42 for(int i=1; i<=n; i++) { 43 vis[i]=1; 44 b[0]=i; 45 dfs(i,1,a[i]); 46 b[0]=0; 47 vis[i]=0; 48 } 49 for(int i=0; i<num; i++) { 50 cout<<c[i]<<' '; 51 } 52 cout<<endl<<ans<<endl; 53 return 0; 54 }

浙公网安备 33010602011771号