hdu3001 Travelling 三进制状态压缩

题意: Mr.Acmer 希望能走遍所有的城市,但他不会进入一个城市超过两次。 每次从一个城市到另一个城市需要一定的消耗,求他总共最少的消耗量。如果他不能在条件允许下走遍所有城市则输出-1。

由数据量可知,城市数量仅为个位数的数量级,非常少。所以可以用状态压缩来做,对每一个城市而言,有三种状态。我们用0表示为走过,1表示走过1次,2表示走过两次。

map[ i ][ j ] 来存储 i 号城市到 j 号城市所需要的消耗。 dp[ i ][ j ] :i 状态下,以 j 号城市为最后落脚点所需的最少消耗量。

dp[ i ][ j ] = min ( dp[ p ][ k ] +map[ k ][ j ] )  

预处理出状态 i 下三进制每一位数,存在s[ i ] 中。

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 0x1f1f1f1f
int three[11],s[59060][11],dp[59060][11];
int n,m,map[11][11];

void pretreat()//预处理每个状态i : 十进制数换三进制数后每一位数什么
{
    int i,p,up;
    three[0]=1;
    for(i=1;i<11;i++)
    {
        three[i]=three[i-1]*3;
    }
    for(i=0;i<three[10];i++)
    {
        p=i;
        up=0;
        while(p!=0)
        {
            s[i][up++]=p%3;
            p/=3;
        }
    }
}

int main()
{
    int i,j,k,r;
    pretreat();
    while(scanf("%d%d",&n,&m)!=EOF)//忘记写!=EOF  我te了16次,还在那不断得各种优化。。我艹。。
    {
        memset(dp,INF,sizeof(dp));
        int x,y;
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
            map[i][j]=INF;
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&x,&y);
            scanf("%d",&map[x-1][y-1]);
            map[y-1][x-1]=map[x-1][y-1]=min(map[x-1][y-1],map[y-1][x-1]);
        }
        for(i=0;i<n;i++)//初始化超人把他送到任意位置开始时的值
        {
            dp[three[i]][i]=0;
        }
        dp[0][0]=0;
        int ans=INF;
        for(i=1;i<three[n];i++)//枚举状态
        {
            int flag=0;
            for(j=0;j<n;j++)//枚举每一个给状态下最后一个到达的点
            {
                if(s[i][j]==0) flag=1;
                if(dp[i][j]==INF)  continue;
                for(k=0;k<n;k++)//枚举下一个将要到达的点
                {
                    if(j==k||s[i][k]==2||map[j][k]==INF)  continue;
                    int ret=i+three[k];
                    dp[ret][k]=min(dp[ret][k],dp[i][j]+map[j][k]);
                }
            }
            if(flag==0)
            {
                for(r=0;r<n;r++)
                {
                    ans=min(ans,dp[i][r]);
                }
            }
        }
        if(ans==INF) printf("-1\n");
        else printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值