题目描述:
地图上有k个点必须走,而且最后要走回起点,这题或许可以用BFS写?没试过,我在这里用的是DP的写法,不过无论是BFS或者DP,核心还是状态压缩,我觉得状态压缩的题数据一般会有十分明显的提示,通常是有一些的物品的处理情况非常多,但是这种特殊物品的数量又特别少,通常是不超过20,这种时候一般都采取状态压缩的方式,将状态简化之后这题其实是一个非常清晰的关于最短路径的DP,算出每一点的最短距离之后,在枚举该点回到起点的距离,最后找到和的最小值就可以了。关于状态压缩在我之前讲POJ2923中已经讲的比较清楚,那个题如果理解了,那么理解状态压缩并不困难 。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
const int MAXM=101;
const int INF=999999;
struct node
{
int x;
int y;
};
int n,m;
int ma[MAXM][MAXM];
struct node p[MAXM];
int dis[MAXM][MAXM];
int dp[3010][MAXM];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
int cnt=0;
memset(p,0,sizeof(p));
for (int i=0;i<n;i++)
{
for (int j=0;j<m;j++)
{
scanf("%d",&ma[i][j]);
if (i==0&&j==0)
{
p[cnt].x=i;
p[cnt].y=j;
cnt++;
}
else if (ma[i][j]!=0)
{
p[cnt].x=i;
p[cnt].y=j;
cnt++;
}
}
}
memset(dis,INF,sizeof(dis));
dis[0][0]=0;
for (int i=0;i<cnt;i++)
{
for (int j=i+1;j<cnt;j++)
{
dis[i][j]=dis[j][i]=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y);
}
}
memset(dp,INF,sizeof(dp));
dp[0][0]=0;
for (int i=0;i<(1<<cnt);i++)
{
for (int j=0;j<cnt;j++)
{
if (dp[i][j]!=INF)
{
for (int k=0;k<cnt;k++)
{
if ((i>>k)&1) continue;
if (dis[j][k]==INF) continue;
dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dis[j][k]);
}
}
}
}
int ans=INF;
for (int i=0;i<cnt;i++)
if (dp[(1<<cnt)-1][i]+dis[i][0]<ans)
ans=dp[(1<<cnt)-1][i]+dis[i][0];
printf("%d\n",ans);
}
return 0;
}