uva 10944 Nuts for nuts..(状压dp)

题意:一幅地图中给出松鼠起点,各坚果的信息,求松鼠收集所有坚果并返回起点的最小步数;

思路:

     用二进制数表示坚果的收集状态,0表示未收集,1已收集;mm[i][j]表示节点i和j的相对距离;f[i][j]表示在收集状态为j是收集i的最小步数;

     显然,收集每颗坚果的最小步数为f[i][2的(i-1)次方]=mm[0][i];

     递增枚举状态值i,状态i中最后被收集的坚果j,枚举i外的坚果k。

     f[k][i+1<<(k-1)]=min( f[k][i+1<<(k-1)],f[j][i]+mm[j][k]);

    所有坚果收集后,若最后一颗为i,则到i的最小步数为f[i][1<<(n)-1],加上返回起点的步数map[0][i],找到最少步数;

   ans=min(f[i][1<<(n)-1]+mm[0][i]);(枚举i)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf (1<<20)
#define N 30
#define M 65536
using namespace std;
int f[N][M];
char s[N];
int mm[N][N];
int x[N],y[N];
int num,n,m,ans,maxz;
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        num=0;
        for(int i=0;i<n;i++)
        {
            scanf("%s",s);
            for(int j=0;j<m;j++)
            {
                if(s[j]=='#'){
                    x[++num]=i;
                    y[num]=j;//坚果位置
                }
                else if(s[j]=='L'){
                    x[0]=i;y[0]=j;//当前位置
                }
            }
        }
        if(!num){
            printf("0\n");continue;
        }
        for(int i=0;i<=num;i++)
            for(int j=0;j<=num;j++)
            mm[i][j]=max(abs(x[i]-x[j]),abs(y[i]-y[j]));//计算节点间相对距离的矩阵
        maxz=(1<<num)-1;
        for(int i=0;i<=maxz;i++){
            for(int j=0;j<=num;j++)
                f[j][i]=inf;
        }
        for(int i=1;i<=num;i++) f[i][1<<(i-1)]=mm[0][i];//只收集第i个坚果的步数
        for(int i=0;i<maxz;i++)  //枚举当前坚果被收集的状态
        {
            for(int j=1;j<=num;j++) if(i&(1<<(j-1)))//枚举最后被收集的坚果j
                for(int k=1;k<=num;k++)  //枚举i状态外的坚果k,调整k被收集的最优步数
                if(!(i&(1<<(k-1)))) f[k][i+(1<<(k-1))]=min(f[k][i+(1<<(k-1))],f[j][i]+mm[j][k]);
        }
        ans=inf;
        for(int i=1;i<=num;i++)  //枚举最后被收集的坚果(返回‘L’位置的最少步数)i,调整最优步数
            ans=min(ans,f[i][maxz]+mm[i][0]);
        printf("%d\n",ans);
    }
    return 0;
}

 

posted on 2015-06-14 01:18  大树置林  阅读(234)  评论(0编辑  收藏  举报

导航