首页 > 代码库 > UVA 10944 Nuts for nuts 经典状压DP

UVA 10944 Nuts for nuts 经典状压DP

https://vjudge.net/problem/UVA-10944

//题意:给出n*m地图,起点为L,每次可以向8个方向移动,地图中有若干个果实,求搜集所有果实后再回到起点的最短路径
//n,m<=20,果实个数<=15 则容易用二进制记录当前拿到的果实
//设dp[i][state]为当前s在果实i&&拿到的果实状态为state
//当2^(i-1) & j==0时,dp[i][j+2^(i-1)]=min(dp[i][j+2^(i-1)],dp[k][j]+dist(k,i)) 按状态增大方向递推即可

#include <bits/stdc++.h>
using namespace std;
const int N=40;
const int inf=1<<20;
const int M=5e6;
char s[N];
int num,n,m,dist[N][N],x[N],y[N];//
int dp[N][M];
void init()
{
    for(int i=0;i<=num;i++)
        for(int j=i;j<=num;j++)
            dist[i][j]=dist[j][i]=max(abs(x[i]-x[j]),abs(y[i]-y[j]));//可以走8方向 
}
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]==L)
                {
                    x[0]=i;
                    y[0]=j;
                }
                else if(s[j]==#)
                {
                    x[++num]=i;
                    y[num]=j;
                }
            }
        }
        if(!num)
        {
            puts("0");
            continue;
        }
        init();//计算果实间距离 
        int state=(1<<num)-1;
        memset(dp,0x7f,sizeof(dp));
        for(int i=1;i<=num;i++)
            dp[i][1<<(i-1)]=dist[0][i];    
        //O(15*15*2^15) 
        for(int i=0;i<state;i++)
        {
            for(int j=1;j<=num;j++)//枚举当前最后一个搜集到的果实 
            {
                if(i&(1<<(j-1)))
                for(int k=1;k<=num;k++)//枚举最后一个以外过时k 
                {
                    if(!(i&(1<<(k-1))))
                        dp[k][i+(1<<(k-1))]=min(dp[k][i+(1<<(k-1))],dp[j][i]+dist[j][k]);
                }
            }
        }
        int ans=inf;
        for(int i=1;i<=num;i++)
            ans=min(ans,dp[i][state]+dist[i][0]);
        printf("%d\n",ans);
    }
    return 0;    
}

 

UVA 10944 Nuts for nuts 经典状压DP