HDU_3001 Travelling C++【状态压缩dp,三进制】(萌新题解,超详细)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3001

题目:

After coding so many days,Mr Acmer wants to have a good rest.So travelling is the best choice!He has decided to visit n cities(he insists on seeing all the cities!And he does not mind which city being his start station because superman can bring him to any city at first but only once.), and of course there are m roads here,following a fee as usual.But Mr Acmer gets bored so easily that he doesn't want to visit a city more than twice!And he is so mean that he wants to minimize the total fee!He is lazy you see.So he turns to you for help.
InputThere are several test cases,the first line is two intergers n(1<=n<=10) and m,which means he needs to visit n cities and there are m roads he can choose,then m lines follow,each line will include three intergers a,b and c(1<=a,b<=n),means there is a road between a and b and the cost is of course c.Input to the End Of File.OutputOutput the minimum fee that he should pay,or -1 if he can't find such a route.Sample Input
2 1
1 2 100
3 2
1 2 40
2 3 50
3 3
1 2 3
1 3 4
2 3 10
Sample Output
100
90
7 

思路:

这是一个状态压缩DP,但是和常规的状态压缩DP不同的是,它是一个三进制。

(奈何我二进制还没研究清楚,先把三进制研究清楚了)

我是在ACM/ICPC算法基础训练教程上刷的这题,上面有这题的解释,(的确比网上的容易理解多了= =)

于是我研究了一个下午,结果如下:

先把每个数的三次幂存在一个数组里

three[0]=1;
    for(int i=1;i<11;i++)
    {
        three[i]=three[i-1]*3;
    }

因为这题最多有10个城市,所以我们把每种压缩状态下每个城市的访问次数记在一个数组wz[i][j]里。表示i状态下j城市的访问次数。

就是这个“状态”我理解了半天。

比如有四个城市,访问次数是1 0 1 1,那么按权相加,它的压缩状态是1*3^0+1*3^1+0*3^2+1*3^3=31,于是当压缩状态为31时,wz[31][0]=1,wz[31][1]=1,wz[31][2]=0,wz[31][3]=1.

for(int i=0;i<three[10];i++)
    {
        int b=i;
        for(int j=0; j<10; j++)
        {
            wz[i][j]=b%3;
            b/=3;
        }
    }

前面两段都是初始化,在程序最开始运行时实现。

我用path[i][j]数组标记两个城市之间的费用,用dp[i][j]存i状态下最后一个城市为j时的费用。

先初始化这两个数组。

for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                path[i][j]=INF;
            }
        }
        for(int i=0;i<three[n];i++)
        {
            for(int j=0;j<n;j++)
            {
                dp[i][j]=INF;
            }
        }

然后处理一下输入的东西。

while(m--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            path[a-1][b-1]=path[b-1][a-1]=min(c,path[a-1][b-1]);
        }

因为每个城市都可以当开始点,所以我们把每个城市作为开始的状态都标记一下,

for(int i=0;i<n;i++)dp[three[i]][i]=0;

用ans来记录最后答案,

int ans=INF;

动态转移的思路是这样的:

for(int i=0;i<three[n];i++)//遍历每种状态
        {
            int flag=1;//作为是否结束标记
            for(int j=0;j<n;j++)
            {
                if(wz[i][j]==0)flag=0;//如果有一个城市没访问到,那么就还没结束
                if(wz[i][j]!=INF)//当j城市在i状态下被访问过时
                {
                    for(int k=0;k<n;k++)//找下一个可以转移的城市
                    {
                        if(path[k][j]!=INF&&wz[i][k]!=2)//这个城市必须和j城市相连,并且在i状态下访问次数不超过两次
                        {
                            dp[i+three[k]][k]=min(dp[i+three[k]][k],dp[i][j]+path[k][j]);//i+three[k]即为下一个状态
                        }
                    }
                }
            }
            if(flag==1)//当i状态下所有城市都访问过时
            {
                for(int j=0;j<n;j++)//依次找最小值
                {
                    ans=min(ans,dp[i][j]);
                }
            }
        }

当ans还是INF时要输出-1.

全部代码如下:

#include<iostream>
#include<cmath>
using namespace std;
int three[11];
int wz[60000][11];
int dp[60000][11];
int path[11][11];
int n,m;
#define INF 0x3f3f3f3f
void zh()
{
    three[0]=1;
    for(int i=1;i<11;i++)
    {
        three[i]=three[i-1]*3;
    }
    for(int i=0;i<three[10];i++)
    {
        int b=i;
        for(int j=0; j<10; j++)
        {
            wz[i][j]=b%3;
            b/=3;
        }
    }
}
int main()
{
    zh();
    while(cin>>n>>m)
    {
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                path[i][j]=INF;
            }
        }
        for(int i=0;i<three[n];i++)
        {
            for(int j=0;j<n;j++)
            {
                dp[i][j]=INF;
            }
        }
        while(m--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            path[a-1][b-1]=path[b-1][a-1]=min(c,path[a-1][b-1]);
        }
        for(int i=0;i<n;i++)dp[three[i]][i]=0;
        int ans=INF;
        for(int i=0;i<three[n];i++)
        {
            int flag=1;
            for(int j=0;j<n;j++)
            {
                if(wz[i][j]==0)flag=0;
                if(wz[i][j]!=INF)
                {
                    for(int k=0;k<n;k++)
                    {
                        if(path[k][j]!=INF&&wz[i][k]!=2)
                        {
                            dp[i+three[k]][k]=min(dp[i+three[k]][k],dp[i][j]+path[k][j]);
                        }
                    }
                }
            }
            if(flag==1)
            {
                for(int j=0;j<n;j++)
                {
                    ans=min(ans,dp[i][j]);
                }
            }
        }
        if(ans>=INF)cout<<-1<<endl;
        else cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Zero_979/article/details/81054421