@wwwqeqeqeqe
2019-05-12T09:13:00.000000Z
字数 7473
阅读 880
搜索
A Sticks (POJ 1011)
题目大意:
给出n个木棍,将这些木棍进行组合,使得所有组合后的木棍长度相同。问,我们能得到的最小的长度是多少?将这个长度输出。
解题思路:
一个很裸的DFS,但是在搜索的过程中,我们需要注意以下几个问题:
1、我们最后得到的长度很显然,最短为我们输入的所有数字中最大的那个,最长为所有木棍的长度总和。
2、在搜索的过程中,当我们发现和某一长度的木棍拼接失败后,就不要再继续和相同长度的木棍进行结合了。
3、在拼木棍是,我们应该先拼长的,后拼短的,因为相对而言,长的木棍更不容易被其他木棍选择进行拼接。
所以,我们可以对得到的木棍进行一个排序,然后在选择木棍时,对我们选择的木棍进行一个标记,如果这个木棍不适合我们这次的拼接,那么,后面相同长度的所有木棍都直接跳过。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;int s[70],vis[70];int n,len,num;bool cmp(int x, int y){return x>y;}int dfs(int c,int k,int cnt){if(cnt == num)return 1;if(c == len)return dfs(0,0,cnt+1);int i,pre=0;for(i=k;i<n;i++){if(vis[i] == 0 && s[i]+c<=len && s[i]!=pre){pre=s[i];vis[i]=1;if(dfs(s[i]+c,i+1,cnt))break;vis[i]=0;if(k == 0)return 0;}}if(i == n)return 0;return 1;}int main(){while(cin >> n && n){memset(s,0,sizeof(s));memset(vis,0,sizeof(vis));int sum=0;for(int i=0;i<n;i++){cin >> s[i];sum+=s[i];}sort(s,s+n,cmp);len=s[0];for(len;len<=sum/2;len++){if(sum%len == 0){num=sum/len;if(dfs(0,0,0))break;}}if(len>sum/2)cout << sum << endl;elsecout << len << endl;}return 0;}
B Best Sequence (POJ 1699)
题目大意:
题目给出n个只包含‘A’,‘C’,‘G’,‘T’的字符串,若某个字符串结尾的几个字符和另一个字符串开头的几个字符相同,他们就可以共用这些字符。问将这些字符串全部结合在一起后的字符串最短的长度。
解题思路:
通过DFS枚举所有的情况,将已经搜索过的字符串做一个标记,我们将已经搜过的串所组成的那个字符串开个数组存起来,每次从这个字符串的结尾和我们搜索的字符串进行比较,如果满足题目条件就往这个字符串里面继续加,直到最后加完所有的字符串为止。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;char str[12][22] ;char s[300], s1[300] ;int vis[12], min1, n, l[12];void dfs(int cnt,int k)//cnt表示组合后的串的长度,k表示组合了这么多个串{if( cnt >= min1 )return ;if(k == n){min1 = min(min1,cnt) ;return ;}int num ;for(int i = 0 ; i < n ; i++){if( vis[i] )continue ;vis[i] = 1 ;for(int p = max(0,cnt-l[i]) ; p <= cnt ; p++){for(int q = p ; q < cnt ; q++)if( str[i][q-p] != s[q] )break ;if( q == cnt ){num = cnt ;for(int q = q-p ; q < l[i] ; q++)s[num++] = str[i][q] ;break ;}}dfs(num,k+1) ;vis[i] = 0 ;}return ;}int main(){int t ;scanf("%d", &t) ;while( t-- ){scanf("%d", &n) ;for(int i = 0 ; i < n ; i++){scanf("%s", str[i]) ;l[i] = strlen(str[i]) ;}min1 = 1000 ;memset(s,0,sizeof(s)) ;memset(vis,0,sizeof(vis)) ;dfs(0,0) ;printf("%d\n", min1) ;}return 0 ;}
C Paid Roads (POJ 3411)
题目大意:
这里有n座城市和m条路,现在要从城市1走到城市n,其中,通过这些路是需要收费的,从城市a到城市b之前如果到过城市c,那么收取过路费p元,否则收取q元。问从城市1到城市n最少需要花费多少钱?
解题思路:
这个题和其他最短路的最大区别在于,这个题并不一定是每条路都走当前最优解进行通行,可能重复通过某些对象能得到更优的解,即可能会走一遍环什么的。但我们又不能始终重复这个操作,这样会死循环下去,于是,我们对所有的点进行一个标记,因为我们的m最大只有10,那么,我们可以设置一个阈值k,当一个人到达这个城市大于k次后,我们则认为他进入了死循环,然后dfs即可。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;const int INF=0x3f3f3f3f;int n,m;int vis[11];int ans;struct node{int a,b,c,p,r;} r[11];void dfs(int x,int cost){if(x==n){if(cost < ans)ans=cost;return;}for(int i=1; i<=m; i++)if(r[i].a==x&&vis[r[i].b]<=3){vis[r[i].b]++;if(vis[r[i].c]>0)dfs(r[i].b,cost+r[i].p);elsedfs(r[i].b,cost+r[i].r);vis[r[i].b]--;}return;}int main(){cin>>n>>m;for(int i=1; i<=m; i++)cin>>r[i].a>>r[i].b>>r[i].c>>r[i].p>>r[i].r;vis[1]=1;ans=INF;dfs(1,0);if(ans==INF)cout<<"impossible"<<endl;elsecout<<ans<<endl;}
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代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std;const int maxn=1e2+5;const int maxk=1e4+5;int len, k, n[maxn], mod[maxn][10];int m[maxn], flag[maxn][maxk];char num[maxn];void init_mod()//mod[i][j]表示(10^i)*j模k的值{for (int i = 0; i <= 9; i++)mod[0][i] = i % k;for (int i = 1; i < len; i++)for (int j = 0; j <= 9; j++)mod[i][j] = (mod[i-1][j] * 10) % k;}int dfs(int pos,int restnum,int m_modk){if (!m_modk)return 1;if (!restnum || pos < 0)return 0;if (restnum <= flag[pos][m_modk])return 0;for (int i = pos; i > -1; i--)//搜索比n小的数,要尽可能小,则从高位开始for (int j = 0; j < n[i]; j++){if (i == len - 1 && !j)continue;int res = (m_modk - (mod[i][n[i]] - mod[i][j]) + k) % k;m[i] = j;if (dfs(i - 1, restnum - 1, res))return 1;m[i] = n[i];}for (int i = 0; i <= pos; i++)//搜索比n大的数,要尽可能小,则从低位开始for (int j = n[i] + 1; j < 10; j++){int res = (m_modk + (mod[i][j] - mod[i][n[i]]) + k) % k;m[i] = j;if (dfs(i - 1, restnum - 1, res))return 1;m[i] = n[i];}flag[pos][m_modk] = restnum;//能运行到这里说明搜索失败,更新剪枝数值return 0;}int main(){while (~scanf("%s%d", num, &k)){int n_modk = 0;len = strlen(num);init_mod();for (int i = 0; i < len; i++)//将num反序存入整型数组{n[i] = num[len-1-i] - '0';m[i] = n[i];n_modk = (n_modk + mod[i][ n[i] ]) % k;//计算n % k}memset(flag, 0, sizeof(flag));int ok = 0;for (int i = 1; i <= len; i++)//从小到大枚举可以修改的位数if (dfs(len - 1, i, n_modk))break;for (int i = len - 1; i > -1; i--)printf("%d", m[i]);printf("\n");}return 0;}
E The Treasure (POJ 1924)
题目大意:
题目给出一张地图,地图中有六种字符,‘#’表示墙,‘p’表示人最初始的位置,‘t’表示终点,‘.’表示可以走的路,‘a’表示有攻击欲望的怪,可以攻击他周围的九个格子,‘n’表示没有攻击欲望的怪,只会攻击自己所处的格子。人每次可以像自己的周围八个方向移动一或两格,或不动。怪兽会在固定的循环内移动自己的位置,题目给出所有怪物的移动轨迹。人每次移动不能穿墙,也不能从会被怪兽攻击的格子中穿过。且每次移动是怪兽先移动,即人站在怪兽下一秒的攻击范围内也会死。问人最少需要多少时间到达终点。题目保证最少时间不会超过100S,如果100S内不能到达终点,则输出impossible。
解题思路:
因为我们的怪物是会动的,而且最多只会有100S,那么我们可以建一个地图来预处理我们的地图,在预处理地图时,要注意每次是怪物先走,要考虑到怪物走了一秒后在人还没动的时候就把人吃掉这一种情况。接下来就是BFS跑图就可以了。
AC代码:
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>#include<queue>using namespace std;const int maxn=1e2+5;int n,m,ans,cnt;int sx,sy,ex,ey;char s[maxn];int walkx[9]= {-1,1,0,0,-1,-1,1,1,0}; // walk stayint walky[9]= {0,0,-1,1,-1,1,-1,1,0};int runx[8]= {-2,2,0,0,-2,-2,2,2}; // runint runy[8]= {0,0,-2,2,-2,2,-2,2};bool mp[maxn][maxn][maxn]; // time x ybool tmp[maxn][maxn];bool vis[maxn][maxn][maxn]; // time x y 三维数组判重bool type[maxn];struct node{int step;int xx,yy;} cur,now;queue<node>q;bool isok(int x1,int y1,int tst,int dir,int flag) // 判断是否能走{int i,j,xx,yy,temp,xx1,yy1;if(x1<1||x1>n||y1<1||y1>m||vis[tst][x1][y1]||tst>100) return false ;if(flag) // 如果是跑的话 还要注意不能穿过墙和怪物级攻击范围{x1-=walkx[dir];y1-=walky[dir];if(mp[tst][x1][y1]) return false ;x1+=walkx[dir];y1+=walky[dir];if(mp[tst][x1][y1]||mp[tst][x1-runx[dir]][y1-runy[dir]])return false ;}else if(mp[tst][x1][y1]||mp[tst][x1-walkx[dir]][y1-walky[dir]])return false ;return true ;}bool bfs(){int i,j;int nx,ny,nstep,tx,ty,tstep;while(!q.empty())q.pop();memset(vis,0,sizeof(vis));cur.xx=sx;cur.yy=sy;cur.step=0;q.push(cur);vis[0][sx][sy]=1;while(!q.empty()){now=q.front();nx=now.xx;ny=now.yy;nstep=now.step;if(nx==ex&&ny==ey){ans=nstep;return true ;}for(i=0; i<9; i++) // 走的8个方向及站着不动{tx=nx+walkx[i];ty=ny+walky[i];tstep=nstep+1;if(isok(tx,ty,tstep,i,0)){vis[tstep][tx][ty]=1;cur.xx=tx;cur.yy=ty;cur.step=tstep;q.push(cur);}if(i<8) // 跑的8个方向{tx=nx+runx[i];ty=ny+runy[i];tstep=nstep+1;if(isok(tx,ty,tstep,i,1)){vis[tstep][tx][ty]=1;cur.xx=tx;cur.yy=ty;cur.step=tstep;q.push(cur);}}}q.pop();}return false ;}int main(){int i,ii,j,jj,p,ss,k=0,monx,mony;while(scanf("%d%d",&n,&m),n||m){memset(tmp,1,sizeof(tmp)); // 存原始地图cnt=0;for(int i=1; i<=n; i++){scanf("%s",s);for(int j=1; j<=m; j++){if(s[j-1]=='.'){tmp[i][j]=0;}else if(s[j-1]=='p'){tmp[i][j]=0;sx=i;sy=j;}else if(s[j-1]=='t'){tmp[i][j]=0;ex=i;ey=j;}else if(s[j-1]=='a'){tmp[i][j]=0;cnt++;type[cnt]=1;}else if(s[j-1]=='n'){tmp[i][j]=0;cnt++;type[cnt]=0;}}}for(int i=1; i<=100; i++){memcpy(mp[i],tmp,sizeof(tmp));}scanf("%d",&p);for(int i=1; i<=p; i++) // 将每一个时间地图的情况都预处理出来 则可将怪物看做墙{scanf("%d",&ss);for(int j=0; j<ss; j++){scanf("%d%d",&monx,&mony);if(type[i]){for(int jj=j; jj<=102; jj+=ss){for(int ii=0; ii<9; ii++){mp[jj][monx+walkx[ii]][mony+walky[ii]]=1;}}}else{for(int jj=j; jj<=102; jj+=ss){mp[jj][monx][mony]=1;}}}}k++;if(k!=1)printf("\n");if(bfs())printf("%d\n",ans);elseprintf("impossible\n");}return 0;}