@wwwqeqeqeqe
2019-05-12T17:13:00.000000Z
字数 7473
阅读 751
搜索
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;
else
cout << 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);
else
dfs(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;
else
cout<<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 stay
int walky[9]= {0,0,-1,1,-1,1,-1,1,0};
int runx[8]= {-2,2,0,0,-2,-2,2,2}; // run
int runy[8]= {0,0,-2,2,-2,2,-2,2};
bool mp[maxn][maxn][maxn]; // time x y
bool 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);
else
printf("impossible\n");
}
return 0;
}