hdu 3001 dp+状态压缩

/*
用三进制进行状态压缩 0表示没有经过这个点 1表示经过一次 2表示两次
然后用dp[i][j] 表示结尾是j,状态是i时的状态得到的最小值
那么状态转移方程 dp[i][k]=max(dp[l][j]+road[j][k])
那么就可以遍历所有状态,比较所有状态中的三进制表示每一位的数大于0的最小值


*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<math.h>
using namespace std;
#define maxn 20
#define maxs 60000
#define inf 0x7fffffff
int dp[maxs][maxn];
int time[maxs][maxn];//time[i][j]表示状态为i是j的次数
int s[maxn];//表示每一位代表的进制
int road[maxn][maxn];
int n;int m;
void init()
{
    int i;int j;
    s[0]=1;
    for(i=1;i<=n;i++)
    s[i]=3*s[i-1];
    for(i=0;i<s[n];i++)
    {
        int t=i;
        for(j=0;j<n;j++)
        {
            time[i][j]=t%3;
            t/=3;
        }
    }
}
void DP()
{
   int ans=inf;
   int i,j;int k;
    init();
   for(i=0;i<s[n];i++)
   for(j=0;j<n;j++)
      dp[i][j]=inf;
   for(i=0;i<n;i++)
   dp[s[i]][i]=0;//初始化dp  
   for(i=0;i<s[n];i++)
   {
       int flag=0;
       for(j=0;j<n;j++)
       {
           if(time[i][j]==0){flag=1;continue;}
           if(dp[i][j]==inf)continue;
           for(k=0;k<n;k++)
           {
               if(time[i][k]==2||j==k)
               continue;
               if(road[j][k]==inf)
               continue;
               int next=i+s[k];
               dp[next][k]=min(dp[next][k],dp[i][j]+road[j][k]);
           }
       }
       if(!flag)
       {
           for(j=0;j<n;j++)
           ans=min(ans,dp[i][j]);
       }
   }
   if(ans==inf)printf("-1\n");
   else printf("%d\n",ans);
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int a,b,c;
        for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        road[i][j]=inf;
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(road[a-1][b-1]>c)
            road[a-1][b-1]=road[b-1][a-1]=c;
        }
        DP();
    }
    return 0;
}





你可能感兴趣的:(hdu 3001 dp+状态压缩)