题目链接:http://acm.hnu.cn/online/?action=problem&type=show&id=12511
题目大意:给定一个矩阵:“#”代表绵羊,“X”代表山不能走,‘’。‘代表草坪能走;“U”代表起始点;问从起始点开始,能否把所有的绵羊都吃掉,
如果能问,最好花多少时间?
解题报告人:GHQ(SpringWater)
解题思路:考虑到绵羊的总是最多为16,将开始点考虑进去共17个点,所以只用先用BFS求出这17个点的相互距离,之后再利用状态压缩,
dp【i】【j】=min(dp【i】【j】,dp【i^(1<<j)】【k】+dis【j】【k】+1);表示,在状态i下,j最后访问,的最小时间消费值;
最终的ans=min(dp【up】【j】)
代码如下:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<queue> using namespace std; const int S = (1 << 17); const int inf = 1<<29; char mat[55][55]; int rem[20], cnt, dis[20][20]; int d[55][55]; int dx[]={0,0,-1,1}; int dy[]={-1,1,0,0}; int dp[S][17]; int N,M; bool isok(int x,int y) { if(x<0||y<0||x>=N||y>=M||mat[x][y]=='X')return false; return true; } void bfs(int x,int y) { int tx,ty,i,j; int xx = x; int yy = y; queue<int>qx,qy; qx.push(x); qy.push(y); for(i = 0; i < N; ++ i) for(j = 0; j < M; ++ j) d[i][j]=inf; d[x][y]=0; while(!qx.empty()){ x = qx.front(); qx.pop(); y = qy.front(); qy.pop(); for(i = 0; i < 4; ++ i){ tx = x + dx[i]; ty = y + dy[i]; if(!isok(tx,ty)||d[x][y]+1>=d[tx][ty])continue; d[tx][ty] = d[x][y] + 1; qx.push(tx); qy.push(ty); } } } int min(int a,int b) { return (a<b)?a:b; } void DP(int up) { int i, k,j; int temp = (up<<1)|1; for(k=0;k<=temp;k++) for(i = 0; i <=cnt; i++)dp[k][i] = inf; dp[1][0] = 0; for(i = 1; i <= up ; ++ i) { temp = (i<<1)|1; for(j = 1; j <= cnt; j++) { if(temp&(1<<j)) { for(k = 0; k <= cnt ;k ++) { if(k!=j) dp[temp][j] =min(dp[temp][j],dp[temp^(1<<j)][k]+dis[j][k]+1); } } } } } int main() { int T, i, j, ans, up; for(scanf("%d", &T); T--; ) { scanf("%d%d",&N, &M); for(cnt = i = 0; i < N; i++) { scanf("%s",mat[i]); for(j = 0; j < M; j++) { if(mat[i][j]=='#') rem[++cnt] = i * M + j; if(mat[i][j]=='U') rem[0] = i * M + j; } } bfs(rem[0]/M, rem[0]%M); for(i = 1; i <= cnt; i++) { if(d[rem[i]/M][rem[i]%M] == inf) break; else dis[0][i] =dis[i][0] = d[rem[i]/M][rem[i]%M]; } if(i > cnt) { for(i = 1; i <= cnt; i++) { bfs(rem[i]/M, rem[i]%M); for(j = 1; j <= cnt; j++) dis[j][i]=dis[i][j]=d[rem[j]/M][rem[j]%M]; } up = ( 1 << (cnt) ) - 1; DP(up); up=(up<<1)|1; for(i=0, ans = inf; i <= cnt; i++) { if(dp[up][i]<ans) ans=dp[up][i]; } printf("%d\n",ans); } else printf("impossible\n"); } return 0; }