7.29 黄昏时刻
(一) 全排列
建模:
给了数字n 代表从1-n 个数全排列
思路:
1. 输入n,如果n值为‘0’,则退出程序
2. vis[i] 保存 是否对第i个数字进行访问
3. dfs 遍历1-n 个数字的全排列,若重复访问,则越过。直到最终访问结束输出访问的的结果。之后回溯删掉对数字的访问。
优化:
none
代码:
1 #include <stdio.h> 2 #include <string.h> 3 4 int a[20] ,n, vis[20]; 5 6 void dfs(int s) { 7 int i; 8 //如果dfs 次数为n,则输出a数组的内容 9 if(s == n) { 10 for(i = 0; i < n; i++) 11 printf("%-5d", a[i]); // -5 是格式化输出左对齐 12 printf(" "); 13 return; 14 } 15 //遍历 1- n 如果访问过则继续循环呗 16 for(i = 1; i <= n; i++) { 17 18 if(vis[i]) continue; 19 vis[i] = 1; //标记节点被访问过 20 a[s] = i; //在a 数组存取全排列的数字 21 dfs(s+1); //深搜 22 vis[i] = 0; //回溯操作,删掉节点访问痕迹 23 } 24 } 25 26 27 int main() { 28 while(1) { 29 scanf("%d", &n); 30 if(!n) break; 31 memset(vis, 0, sizeof(vis)); 32 dfs(0); 33 } 34 return 0; 35 }
(二)全组合
建模:
给了数值n 对n 进行组合,并打印出。
思路:
1.输入n , 如果n不存在 返回。
2.dfs 找出n个数字的组合,用‘0’,‘1’标记法判断一个数字是否存在,并且对存在或者不存在依次进行递归。
3.若dfs次数>n,则输出组合的结果。
代码:
1 #include <stdio.h> 2 3 int n, a[20]; 4 5 void dfs(int s) { 6 int i; 7 //输出排列后的结果 8 if(s > n) { 9 for(i = 1; i <= n; i++) { 10 if(a[i]) 11 printf("%-5d ", i); 12 } 13 return; 14 } 15 //对a[s]的存在亦或是不存在的两种情况进行递归。 16 a[s] = 0; 17 dfs(s+1); 18 a[s] = 1; 19 dfs(s+1); 20 } 21 22 int main() { 23 24 while(1) { 25 scanf("%d", &n); 26 if(!n) break; 27 dfs(1); 28 } 29 30 return 0; 31 }
(D89) The settlers of catan
建模:
已知n 个点,m 条边,输入两个点来构成一条边,构成一副无向图,两点之间可以有多条边,从任意一个点搜索,要求每条边只能经过一次,找到最长的路径。
思路:
1.输入点的个数n, 边的个数m,并且初始化存整个无向图的二维数组w
2.存边进入二维数组W
3.对所有点进行dfs搜索,如果路径最长则更新max
4.dfs找出所有存在的边,并且记录路径的长度
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #define N 26 4 5 int w[N][N],max,n; //w[N][N] 存入整个图,w[i][j]=x 代表i到j有x条边 6 7 void dfs(int,int); 8 9 int main() 10 { 11 int m,sum; 12 int a,b,i; 13 while(1) 14 { 15 scanf("%d%d",&n,&m); 16 if(!m && !n) 17 break; 18 19 memset(w,0,sizeof(w)); //初始化 20 21 for(i=0;i<m;i++) 22 { 23 scanf("%d%d",&a,&b); 24 w[a][b]++; //无向图 25 w[b][a]++; 26 } 27 28 max=0; 29 for(i=0;i<n;i++) 30 { 31 sum=0; //把每个点作为出发点,对每一个点进行搜索 32 dfs(i,sum); 33 } 34 printf("%d ",max); 35 } 36 return 0; 37 } 38 39 void dfs(int a,int sum) // a 为当前探索到哪一个节点,sum为当前探索到的路径长度 40 { 41 int i; 42 if(sum>max) //更新当前搜索到的最长路径 43 max=sum; 44 for(i=0;i<n;i++) 45 { 46 if(w[a][i]) 47 { 48 w[a][i]--; w[i][a]--; //用去一条边 49 dfs(i,sum+1); // 进行下一层递归 50 w[a][i]++; w[i][a]++; //回溯到上一层 51 52 } 53 } 54 return; 55 }
(A38) A long stick
建模:
给了n个棍子的长度, 和b的值
找出一些棍子链接起来得到长度length, length >= b, length尽量接近b
思路:
1. min为结果, 初始化为所有棍子的长度和。
2. dfs找出这堆棍子的组合, 判断每一种组合所得到的长度sum, sum与min相比, 如果sum比min更优, 更新min为sum
优化:
1. 剪枝
a) 在当前搜索到的状态下, 假设剩下的棍子全部装完, 得到的结果 < b, 则可以剪枝.
b) 当搜索到的sum >= b时, 判断sum 与 min, 然后剪枝
2. 排序, 将棍子长度逆序排序, 在搜索的过程中, sum的增长速度会更快, 也可以提高剪枝效率。
未优化的代码:
1 #include <stdio.h> 2 3 int n, b; 4 int len[28]; 5 int min; 6 7 void dfs(int ,int); 8 9 int main() { 10 int t, i; 11 scanf("%d", &t);//输入t次后结束 12 while(t--) { 13 14 scanf("%d%d", &n, &b);//n为小棍子数目,b为拼接后的长度最接近的值 15 min = 0;//拼接后趋近的结果 16 for(i = 0; i < n; i++) {//输入n组小棍子长度 17 scanf("%d", &len[i]); 18 min += len[i]; 19 } 20 21 dfs(0,0);//递归求最接近b长度的若干根棍子合起来的长度值 22 printf("%d ", min);//输出结果 23 } 24 25 return 0; 26 } 27 28 void dfs(int sum, int i) { 29 30 if(min == b) //假如拼接后的长度min 刚好为b 则返回 31 return; 32 if(sum >= b) {//如果棍子拼接的长度刚好>=b 如果比之前拼接的长度更接近于b,就更新min 33 if(sum < min) 34 min = sum; 35 return; 36 } 37 if(i == n) return;//避免死递归判断假如存在n的情况 38 dfs(sum+len[i], i+1);//如果存在第i根棍子,往下搜索求和 39 dfs(sum, i+1);//假设不存在第i根棍子,继续搜索求和 40 }
剪枝后的优化:
1 #include <stdio.h> 2 #include <string.h> 3 4 int n, b; 5 int len[28], s[28]; 6 int min; 7 8 void dfs(int sum, int i); 9 10 int main() { 11 int t, i; 12 scanf("%d", &t); 13 while(t--) { 14 15 scanf("%d%d", &n, &b); 16 min = 0; 17 memset(s, 0, sizeof(s)); 18 for(i = 0; i < n; i++) { 19 scanf("%d", &len[i]); 20 min += len[i]; 21 } 22 for(i = n-1; i > -1; i--) //记录最后一根棍子 to 第i根棍子 的总长度 23 s[i] = s[i+1] + len[i]; 24 25 dfs(0,0); 26 printf("%d ", min); 27 } 28 return 0; 29 } 30 31 void dfs(int sum, int i) { 32 if(min == b) 33 return; 34 if(sum >= b) { 35 if(sum < min) 36 min = sum; 37 return; 38 } 39 if(i == n) return; 40 if(sum+len[i]+s[i+1] >= b ) //剪枝 41 dfs(sum+len[i], i+1); 42 if(sum+s[i+1] >= b) //剪枝 43 dfs(sum, i+1); 44 45 }
剪枝排序后的优化:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int n, b; 6 int len[28], s[28]; 7 int min; 8 9 void dfs(int sum, int i); 10 11 int cmp(const void *a,const void *b) 12 { 13 return *(int *)b-*(int*)a; 14 } 15 16 17 18 int main() { 19 int t, i; 20 scanf("%d", &t); 21 while(t--) { 22 23 scanf("%d%d", &n, &b); 24 min = 0; 25 memset(s, 0, sizeof(s)); 26 for(i = 0; i < n; i++) { 27 scanf("%d", &len[i]); 28 min += len[i]; 29 } 30 31 qsort(len,n,sizeof(len[0]),cmp); 32 33 34 for(i = n-1; i > -1; i--) //记录最后一根棍子 to 第i根棍子 的总长度 35 s[i] = s[i+1] + len[i]; 36 37 dfs(0,0); 38 printf("%d ", min); 39 } 40 return 0; 41 } 42 43 void dfs(int sum, int i) { 44 if(min == b) 45 return; 46 if(sum >= b) { 47 if(sum < min) 48 min = sum; 49 return; 50 } 51 if(i == n) return; 52 if(sum+len[i]+s[i+1] >= b ) //剪枝 53 dfs(sum+len[i], i+1); 54 if(sum+s[i+1] >= b) //剪枝 55 dfs(sum, i+1); 56 57 }
DFS 题目 用到的DFS函数:
1 // The Settlers of Catan V1 2 3 void dfs(int a,int sum) 4 { 5 int i; 6 if(sum>max) max=sum; 7 for(i=0;i<n;i++) 8 9 if(w[a][i]){ 10 w[a][i]--; w[i][a]--; 11 dfs(i,sum+1); 12 w[a][i]++; w[i][a]++; 13 } 14 15 return; 16 }
1 // The Settlers of Catan V2 2 3 void dfs(int u, int num){ 4 int flag; 5 for(int v=0; v<n; ++v){ 6 if(G[u][v] && !vis[u][v]){ 7 8 if(G[u][v] != 1){ 9 flag = 1; 10 vis[u][v] = 0; 11 vis[u][v] = 0; 12 G[u][v] --; 13 G[v][u] --; 14 } 15 else { 16 vis[u][v] = 1; 17 vis[v][u] = 1; 18 19 } 20 21 dfs(v, num+1); 22 if(flag == 1){ 23 G[u][v]++; 24 G[v][u]++; 25 } 26 flag = 0; 27 vis[u][v] = vis[v][u] = 0; 28 } 29 } 30 31 if(num > maxNum) maxNum = num; 32 }
1 // A long stick 2 3 void dfs(int sum,int i) 4 { 5 int j; 6 sum-=stick[i]; 7 if(sum<length || min==length) 8 return; 9 10 if(sum<min) 11 min=sum; 12 13 for(j=i+1;j<=n;j++) 14 dfs(sum,j); 15 }
1 // repeatless V1 2 3 void dfs(int dep) 4 { 5 int i; 6 if (dep==10) 7 { 8 int tmp=d[1]; 9 for (i=2;i<=10;i++) tmp=tmp*10+d[i]; 10 T++; 11 f[T]=tmp; 12 return; 13 } 14 if (T>1000010) return; 15 for (i=0;i<=9;i++) 16 if (s[i]==0) 17 { 18 d[dep+1]=i; 19 sum+=i; 20 if (sum) s[i]++; 21 dfs(dep+1); 22 if (sum) s[i]--; 23 sum-=i; 24 } 25 }
1 // repeatless V2 author : ZSX 2 3 void recursion( int k ) 4 { 5 if( bl == 0 ) 6 return ; 7 int i, j; 8 int a, b; 9 if( p == k ) 10 { 11 int sum = 0; 12 int temp; 13 for( i=0; i<p; i++ ) 14 { 15 temp = 1; 16 for( j=0; j<p-i-1; j++ ) 17 temp *= 10; 18 sum += temp*buffer[i]; 19 } 20 if( q <= 1000000) 21 array[q++] = sum; 22 else 23 bl = 0; 24 } 25 else 26 { 27 for( a=0; a<=9; a++ ) 28 if( flag[a] == 0 && (p != 0 || a!=0) ) 29 { 30 flag[a] = 1; 31 buffer[p++] = a; 32 recursion( k ); 33 flag[a] = 0; 34 p--; 35 } 36 } 37 }
1 // 全排列 2 3 void dfs(int s, int n) { 4 int i; 5 if(s == n) { 6 for(i = 0; i < n; i++) 7 if(i==0) printf("%d", a[i]); 8 else printf(" %d", a[i]); 9 printf(" "); 10 return; 11 } 12 for(i = 1; i <= n; i++) { 13 if(vis[i]) continue; 14 a[s] = i; 15 vis[i] = 1; 16 dfs(s+1, n); 17 vis[i] = 0; 18 } 19 }