[关闭]
@wwwqeqeqeqe 2019-05-12T09:13:00.000000Z 字数 7473 阅读 696

搜索问题讨论(2019.5.14)

搜索


A Sticks (POJ 1011)

题目大意:

给出n个木棍,将这些木棍进行组合,使得所有组合后的木棍长度相同。问,我们能得到的最小的长度是多少?将这个长度输出。

解题思路:

一个很裸的DFS,但是在搜索的过程中,我们需要注意以下几个问题:
1、我们最后得到的长度很显然,最短为我们输入的所有数字中最大的那个,最长为所有木棍的长度总和。
2、在搜索的过程中,当我们发现和某一长度的木棍拼接失败后,就不要再继续和相同长度的木棍进行结合了。
3、在拼木棍是,我们应该先拼长的,后拼短的,因为相对而言,长的木棍更不容易被其他木棍选择进行拼接。
所以,我们可以对得到的木棍进行一个排序,然后在选择木棍时,对我们选择的木棍进行一个标记,如果这个木棍不适合我们这次的拼接,那么,后面相同长度的所有木棍都直接跳过。

AC代码:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. int s[70],vis[70];
  8. int n,len,num;
  9. bool cmp(int x, int y)
  10. {
  11. return x>y;
  12. }
  13. int dfs(int c,int k,int cnt)
  14. {
  15. if(cnt == num)
  16. return 1;
  17. if(c == len)
  18. return dfs(0,0,cnt+1);
  19. int i,pre=0;
  20. for(i=k;i<n;i++)
  21. {
  22. if(vis[i] == 0 && s[i]+c<=len && s[i]!=pre)
  23. {
  24. pre=s[i];
  25. vis[i]=1;
  26. if(dfs(s[i]+c,i+1,cnt))
  27. break;
  28. vis[i]=0;
  29. if(k == 0)
  30. return 0;
  31. }
  32. }
  33. if(i == n)
  34. return 0;
  35. return 1;
  36. }
  37. int main()
  38. {
  39. while(cin >> n && n)
  40. {
  41. memset(s,0,sizeof(s));
  42. memset(vis,0,sizeof(vis));
  43. int sum=0;
  44. for(int i=0;i<n;i++)
  45. {
  46. cin >> s[i];
  47. sum+=s[i];
  48. }
  49. sort(s,s+n,cmp);
  50. len=s[0];
  51. for(len;len<=sum/2;len++)
  52. {
  53. if(sum%len == 0)
  54. {
  55. num=sum/len;
  56. if(dfs(0,0,0))
  57. break;
  58. }
  59. }
  60. if(len>sum/2)
  61. cout << sum << endl;
  62. else
  63. cout << len << endl;
  64. }
  65. return 0;
  66. }

B Best Sequence (POJ 1699)

题目大意:

题目给出n个只包含‘A’,‘C’,‘G’,‘T’的字符串,若某个字符串结尾的几个字符和另一个字符串开头的几个字符相同,他们就可以共用这些字符。问将这些字符串全部结合在一起后的字符串最短的长度。

解题思路:

通过DFS枚举所有的情况,将已经搜索过的字符串做一个标记,我们将已经搜过的串所组成的那个字符串开个数组存起来,每次从这个字符串的结尾和我们搜索的字符串进行比较,如果满足题目条件就往这个字符串里面继续加,直到最后加完所有的字符串为止。

AC代码:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. char str[12][22] ;
  8. char s[300], s1[300] ;
  9. int vis[12], min1, n, l[12];
  10. void dfs(int cnt,int k)//cnt表示组合后的串的长度,k表示组合了这么多个串
  11. {
  12. if( cnt >= min1 )
  13. return ;
  14. if(k == n)
  15. {
  16. min1 = min(min1,cnt) ;
  17. return ;
  18. }
  19. int num ;
  20. for(int i = 0 ; i < n ; i++)
  21. {
  22. if( vis[i] )
  23. continue ;
  24. vis[i] = 1 ;
  25. for(int p = max(0,cnt-l[i]) ; p <= cnt ; p++)
  26. {
  27. for(int q = p ; q < cnt ; q++)
  28. if( str[i][q-p] != s[q] )
  29. break ;
  30. if( q == cnt )
  31. {
  32. num = cnt ;
  33. for(int q = q-p ; q < l[i] ; q++)
  34. s[num++] = str[i][q] ;
  35. break ;
  36. }
  37. }
  38. dfs(num,k+1) ;
  39. vis[i] = 0 ;
  40. }
  41. return ;
  42. }
  43. int main()
  44. {
  45. int t ;
  46. scanf("%d", &t) ;
  47. while( t-- )
  48. {
  49. scanf("%d", &n) ;
  50. for(int i = 0 ; i < n ; i++)
  51. {
  52. scanf("%s", str[i]) ;
  53. l[i] = strlen(str[i]) ;
  54. }
  55. min1 = 1000 ;
  56. memset(s,0,sizeof(s)) ;
  57. memset(vis,0,sizeof(vis)) ;
  58. dfs(0,0) ;
  59. printf("%d\n", min1) ;
  60. }
  61. return 0 ;
  62. }

C Paid Roads (POJ 3411)
题目大意:

这里有n座城市和m条路,现在要从城市1走到城市n,其中,通过这些路是需要收费的,从城市a到城市b之前如果到过城市c,那么收取过路费p元,否则收取q元。问从城市1到城市n最少需要花费多少钱?

解题思路:

这个题和其他最短路的最大区别在于,这个题并不一定是每条路都走当前最优解进行通行,可能重复通过某些对象能得到更优的解,即可能会走一遍环什么的。但我们又不能始终重复这个操作,这样会死循环下去,于是,我们对所有的点进行一个标记,因为我们的m最大只有10,那么,我们可以设置一个阈值k,当一个人到达这个城市大于k次后,我们则认为他进入了死循环,然后dfs即可。

AC代码:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. const int INF=0x3f3f3f3f;
  8. int n,m;
  9. int vis[11];
  10. int ans;
  11. struct node
  12. {
  13. int a,b,c,p,r;
  14. } r[11];
  15. void dfs(int x,int cost)
  16. {
  17. if(x==n)
  18. {
  19. if(cost < ans)
  20. ans=cost;
  21. return;
  22. }
  23. for(int i=1; i<=m; i++)
  24. if(r[i].a==x&&vis[r[i].b]<=3)
  25. {
  26. vis[r[i].b]++;
  27. if(vis[r[i].c]>0)
  28. dfs(r[i].b,cost+r[i].p);
  29. else
  30. dfs(r[i].b,cost+r[i].r);
  31. vis[r[i].b]--;
  32. }
  33. return;
  34. }
  35. int main()
  36. {
  37. cin>>n>>m;
  38. for(int i=1; i<=m; i++)
  39. cin>>r[i].a>>r[i].b>>r[i].c>>r[i].p>>r[i].r;
  40. vis[1]=1;
  41. ans=INF;
  42. dfs(1,0);
  43. if(ans==INF)
  44. cout<<"impossible"<<endl;
  45. else
  46. cout<<ans<<endl;
  47. }

D Changing Digits (POJ 3373)

题目大意:

题目给出两个数字n和k,要求出满足一下条件的数字m。
1、m的位数和n相同。
2、m能被k整除。
3、在满足前两个条件的基础下,m和n在相同位置的地方,数字不同的个数最少。
4、在满足前三个条件的情况下,m尽量小。

解题思路:

我们可以通过DFS对这些条件进行一步一步的运算,首先要满足m最高位非0(除去m=0)且和n位数相同,m能被k整除,在满足m和n在对应位置上不相等数量尽量少,再满足m尽量小。由于n这个数字很大,我们可以需要通过数组对数据进行存储,因为在最后, 我们是计算对k取模的值,那么我们可以开一个数组mod[i][j]用来预处理(10^i)*j模k的值。这样,在改变某一位后,m%k的值也能较快的计算出来。为了使得到的m最小,我们先从m<n开始搜索,再从m>n搜索,其中,当m<n时,res=(m_modk-(mod[i][n[i]]-mod[i][j]+k)%k,当m>n时,res=(m_modk+(mod[i][j]-mod[i][n[i]+k)%k,并对修改过的位打上标记。令引入flag数组进行剪枝,当搜索区间为[0,pos]且此时m%k=m_modk时,如果最多修改restnum位不能成功,那么,修改小于这个位数的位也是不能成功的,就不用继续搜索了。

AC代码:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. const int maxn=1e2+5;
  8. const int maxk=1e4+5;
  9. int len, k, n[maxn], mod[maxn][10];
  10. int m[maxn], flag[maxn][maxk];
  11. char num[maxn];
  12. void init_mod()//mod[i][j]表示(10^i)*j模k的值
  13. {
  14. for (int i = 0; i <= 9; i++)
  15. mod[0][i] = i % k;
  16. for (int i = 1; i < len; i++)
  17. for (int j = 0; j <= 9; j++)
  18. mod[i][j] = (mod[i-1][j] * 10) % k;
  19. }
  20. int dfs(int pos,int restnum,int m_modk)
  21. {
  22. if (!m_modk)
  23. return 1;
  24. if (!restnum || pos < 0)
  25. return 0;
  26. if (restnum <= flag[pos][m_modk])
  27. return 0;
  28. for (int i = pos; i > -1; i--)//搜索比n小的数,要尽可能小,则从高位开始
  29. for (int j = 0; j < n[i]; j++)
  30. {
  31. if (i == len - 1 && !j)
  32. continue;
  33. int res = (m_modk - (mod[i][n[i]] - mod[i][j]) + k) % k;
  34. m[i] = j;
  35. if (dfs(i - 1, restnum - 1, res))
  36. return 1;
  37. m[i] = n[i];
  38. }
  39. for (int i = 0; i <= pos; i++)//搜索比n大的数,要尽可能小,则从低位开始
  40. for (int j = n[i] + 1; j < 10; j++)
  41. {
  42. int res = (m_modk + (mod[i][j] - mod[i][n[i]]) + k) % k;
  43. m[i] = j;
  44. if (dfs(i - 1, restnum - 1, res))
  45. return 1;
  46. m[i] = n[i];
  47. }
  48. flag[pos][m_modk] = restnum;//能运行到这里说明搜索失败,更新剪枝数值
  49. return 0;
  50. }
  51. int main()
  52. {
  53. while (~scanf("%s%d", num, &k))
  54. {
  55. int n_modk = 0;
  56. len = strlen(num);
  57. init_mod();
  58. for (int i = 0; i < len; i++)//将num反序存入整型数组
  59. {
  60. n[i] = num[len-1-i] - '0';
  61. m[i] = n[i];
  62. n_modk = (n_modk + mod[i][ n[i] ]) % k;//计算n % k
  63. }
  64. memset(flag, 0, sizeof(flag));
  65. int ok = 0;
  66. for (int i = 1; i <= len; i++)//从小到大枚举可以修改的位数
  67. if (dfs(len - 1, i, n_modk))
  68. break;
  69. for (int i = len - 1; i > -1; i--)
  70. printf("%d", m[i]);
  71. printf("\n");
  72. }
  73. return 0;
  74. }

E The Treasure (POJ 1924)

题目大意:

题目给出一张地图,地图中有六种字符,‘#’表示墙,‘p’表示人最初始的位置,‘t’表示终点,‘.’表示可以走的路,‘a’表示有攻击欲望的怪,可以攻击他周围的九个格子,‘n’表示没有攻击欲望的怪,只会攻击自己所处的格子。人每次可以像自己的周围八个方向移动一或两格,或不动。怪兽会在固定的循环内移动自己的位置,题目给出所有怪物的移动轨迹。人每次移动不能穿墙,也不能从会被怪兽攻击的格子中穿过。且每次移动是怪兽先移动,即人站在怪兽下一秒的攻击范围内也会死。问人最少需要多少时间到达终点。题目保证最少时间不会超过100S,如果100S内不能到达终点,则输出impossible。

解题思路:

因为我们的怪物是会动的,而且最多只会有100S,那么我们可以建一个地图来预处理我们的地图,在预处理地图时,要注意每次是怪物先走,要考虑到怪物走了一秒后在人还没动的时候就把人吃掉这一种情况。接下来就是BFS跑图就可以了。

AC代码:

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<queue>
  7. using namespace std;
  8. const int maxn=1e2+5;
  9. int n,m,ans,cnt;
  10. int sx,sy,ex,ey;
  11. char s[maxn];
  12. int walkx[9]= {-1,1,0,0,-1,-1,1,1,0}; // walk stay
  13. int walky[9]= {0,0,-1,1,-1,1,-1,1,0};
  14. int runx[8]= {-2,2,0,0,-2,-2,2,2}; // run
  15. int runy[8]= {0,0,-2,2,-2,2,-2,2};
  16. bool mp[maxn][maxn][maxn]; // time x y
  17. bool tmp[maxn][maxn];
  18. bool vis[maxn][maxn][maxn]; // time x y 三维数组判重
  19. bool type[maxn];
  20. struct node
  21. {
  22. int step;
  23. int xx,yy;
  24. } cur,now;
  25. queue<node>q;
  26. bool isok(int x1,int y1,int tst,int dir,int flag) // 判断是否能走
  27. {
  28. int i,j,xx,yy,temp,xx1,yy1;
  29. if(x1<1||x1>n||y1<1||y1>m||vis[tst][x1][y1]||tst>100) return false ;
  30. if(flag) // 如果是跑的话 还要注意不能穿过墙和怪物级攻击范围
  31. {
  32. x1-=walkx[dir];
  33. y1-=walky[dir];
  34. if(mp[tst][x1][y1]) return false ;
  35. x1+=walkx[dir];
  36. y1+=walky[dir];
  37. if(mp[tst][x1][y1]||mp[tst][x1-runx[dir]][y1-runy[dir]])
  38. return false ;
  39. }
  40. else if(mp[tst][x1][y1]||mp[tst][x1-walkx[dir]][y1-walky[dir]])
  41. return false ;
  42. return true ;
  43. }
  44. bool bfs()
  45. {
  46. int i,j;
  47. int nx,ny,nstep,tx,ty,tstep;
  48. while(!q.empty())
  49. q.pop();
  50. memset(vis,0,sizeof(vis));
  51. cur.xx=sx;
  52. cur.yy=sy;
  53. cur.step=0;
  54. q.push(cur);
  55. vis[0][sx][sy]=1;
  56. while(!q.empty())
  57. {
  58. now=q.front();
  59. nx=now.xx;
  60. ny=now.yy;
  61. nstep=now.step;
  62. if(nx==ex&&ny==ey)
  63. {
  64. ans=nstep;
  65. return true ;
  66. }
  67. for(i=0; i<9; i++) // 走的8个方向及站着不动
  68. {
  69. tx=nx+walkx[i];
  70. ty=ny+walky[i];
  71. tstep=nstep+1;
  72. if(isok(tx,ty,tstep,i,0))
  73. {
  74. vis[tstep][tx][ty]=1;
  75. cur.xx=tx;
  76. cur.yy=ty;
  77. cur.step=tstep;
  78. q.push(cur);
  79. }
  80. if(i<8) // 跑的8个方向
  81. {
  82. tx=nx+runx[i];
  83. ty=ny+runy[i];
  84. tstep=nstep+1;
  85. if(isok(tx,ty,tstep,i,1))
  86. {
  87. vis[tstep][tx][ty]=1;
  88. cur.xx=tx;
  89. cur.yy=ty;
  90. cur.step=tstep;
  91. q.push(cur);
  92. }
  93. }
  94. }
  95. q.pop();
  96. }
  97. return false ;
  98. }
  99. int main()
  100. {
  101. int i,ii,j,jj,p,ss,k=0,monx,mony;
  102. while(scanf("%d%d",&n,&m),n||m)
  103. {
  104. memset(tmp,1,sizeof(tmp)); // 存原始地图
  105. cnt=0;
  106. for(int i=1; i<=n; i++)
  107. {
  108. scanf("%s",s);
  109. for(int j=1; j<=m; j++)
  110. {
  111. if(s[j-1]=='.')
  112. {
  113. tmp[i][j]=0;
  114. }
  115. else if(s[j-1]=='p')
  116. {
  117. tmp[i][j]=0;
  118. sx=i;
  119. sy=j;
  120. }
  121. else if(s[j-1]=='t')
  122. {
  123. tmp[i][j]=0;
  124. ex=i;
  125. ey=j;
  126. }
  127. else if(s[j-1]=='a')
  128. {
  129. tmp[i][j]=0;
  130. cnt++;
  131. type[cnt]=1;
  132. }
  133. else if(s[j-1]=='n')
  134. {
  135. tmp[i][j]=0;
  136. cnt++;
  137. type[cnt]=0;
  138. }
  139. }
  140. }
  141. for(int i=1; i<=100; i++)
  142. {
  143. memcpy(mp[i],tmp,sizeof(tmp));
  144. }
  145. scanf("%d",&p);
  146. for(int i=1; i<=p; i++) // 将每一个时间地图的情况都预处理出来 则可将怪物看做墙
  147. {
  148. scanf("%d",&ss);
  149. for(int j=0; j<ss; j++)
  150. {
  151. scanf("%d%d",&monx,&mony);
  152. if(type[i])
  153. {
  154. for(int jj=j; jj<=102; jj+=ss)
  155. {
  156. for(int ii=0; ii<9; ii++)
  157. {
  158. mp[jj][monx+walkx[ii]][mony+walky[ii]]=1;
  159. }
  160. }
  161. }
  162. else
  163. {
  164. for(int jj=j; jj<=102; jj+=ss)
  165. {
  166. mp[jj][monx][mony]=1;
  167. }
  168. }
  169. }
  170. }
  171. k++;
  172. if(k!=1)
  173. printf("\n");
  174. if(bfs())
  175. printf("%d\n",ans);
  176. else
  177. printf("impossible\n");
  178. }
  179. return 0;
  180. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注