http://acm.hdu.edu.cn/showproblem.php?pid=3681
5 5 GDDSS SSSFS SYGYS SGSYS SSYSS 0 0
4
一个人要从F出发,每次只能移动周围的四个相邻的格子,没走一步消耗1单位的能量;当走到G的时候能量可以充到最大值W;问要把所有的Y都关掉的情况下;W最小是多少;否则输出-1;
分析:首先Y和G的总数小于15,可以把Y与G从图中抽取出来,然后利用bfs求得Y与G两两之间的最短路径存在dis数组中;然后二分枚举W,判断条件是状态压缩DP的值是否满足一定条件,Y状态一定要走完,但是G不一定要走完;
程序:
#include"stdio.h" #include"string.h" #include"iostream" #include"map" #include"string" #include"queue" #include"stdlib.h" #include"algorithm" #include"math.h" #define M (1<<15)+2 #define eps 1e-10 #define inf 100000000 #define mod 100000000 #define INF 0x3f3f3f3f using namespace std; int dp[M][16],dis[16][16],mark[16],dist[300][300],px[17],path[M][18],vis[20][20],use[20],est; char mp[17][17]; int n,m; int disx[5]={0,1,0,-1}; int disy[5]={1,0,-1,0}; struct node { int x,y,val; }p[16]; struct Node { int x,y,t; friend bool operator<(Node a,Node b) { return a.t>b.t; } }; void bfs(int x,int y) { priority_queue<Node>q; memset(vis,0,sizeof(vis)); memset(dist,INF,sizeof(dist)); Node now; now.x=x; now.y=y; now.t=0; q.push(now); vis[x][y]=1; while(!q.empty()) { Node cur=q.top(); q.pop(); for(int i=0;i<4;i++) { now.x=cur.x+disx[i]; now.y=cur.y+disy[i]; if(mp[now.x][now.y]=='D')continue; if(now.x<0||now.y<0||now.x>=n||now.y>=m)continue; now.t=cur.t+1; if(dist[now.x][now.y]>now.t) dist[now.x][now.y]=now.t; if(vis[now.x][now.y]==0) { vis[now.x][now.y]=1; q.push(now); } } } } int DP(int cnt,int mid)//DP状压 { int i,j,k,ff; memset(dp,INF,sizeof(dp)); ff=0; for(i=0;i<cnt;i++) if(p[i].val==1) { ff|=(1<<i);//ff存起点F的位置 dp[1<<i][i]=0;//初始化 } for(i=0;i<px[cnt];i++) { if((i&ff)!=ff)continue;//当没有起始位置的状态时跳过 for(j=0;j<cnt;j++) { int tep=i&(1<<j); if(tep==0)continue; int cur=i^(1<<j); for(k=0;k<cnt;k++) { int tmp=i&(1<<k); if(tmp==0||k==j)continue; if(dp[i][j]>dp[cur][k]+dis[k][j]) { if(dp[cur][k]+dis[k][j]<=mid)//在能量承受的范围内可以到达状态dp[i][j]更新; { dp[i][j]=dp[cur][k]+dis[k][j]; if(p[j].val==2)//如果j刚好是G则能量充满,此时把dp置为0; dp[i][j]=0; } } } if((est&i)==est)//判断Y是否经过完 { if(dp[i][j]<INF) return 1; } /**************第二种写法********************/ /*for(k=0;k<cnt;k++) { int tmp=i&(1<<k); if(tmp==0||k==j)continue; if(dp[cur][k]>=dis[k][j])//比较需要消耗的能量是不是大于剩余的能量; { dp[i][j]=max(dp[i][j],dp[cur][k]-dis[j][k]); if(p[j].val==2)//如果j刚好是G则能量充满,此时把dp能量充满; dp[i][j]=mid; } } if((est&i)==est)//判断Y是否经过完 { if(dp[i][j]>=0)//如果不是-1则完成 return 1; }*/ /*******************************************/ } } return 0; } int b[M]; int main() { int i,j; px[0]=1; for(i=1;i<=15;i++) px[i]=px[i-1]*2; while(scanf("%d%d",&n,&m),m||n) { for(i=0;i<n;i++) scanf("%s",mp[i]); int cnt=0; est=0; memset(p,0,sizeof(p)); for(i=0;i<n;i++)//从图中抽取出F,Y,G三个状态; { for(j=0;j<m;j++) { if(mp[i][j]=='F') { est=est|(1<<cnt); p[cnt].x=i; p[cnt].y=j; p[cnt].val=1; cnt++; } else if(mp[i][j]=='G') { p[cnt].x=i; p[cnt].y=j; p[cnt].val=2; cnt++; } else if(mp[i][j]=='Y') { est=est|(1<<cnt); p[cnt].x=i; p[cnt].y=j; p[cnt].val=3; cnt++; } } } memset(dis,INF,sizeof(dis)); for(i=0;i<cnt;i++) { bfs(p[i].x,p[i].y); for(j=0;j<cnt;j++) { if(i==j)dis[i][j]=INF; else dis[i][j]=dist[p[j].x][p[j].y]; } }//bfs求最短路 int left=0; int right=m*n; int mid,ans=-1; while(left<=right)//二分枚举W { mid=(left+right)/2; if(DP(cnt,mid)) { ans=mid; right=mid-1; } else left=mid+1; } printf("%d\n",ans); } return 0; }