hdu 3001 3进制状态压缩dp,TSP

做这题前,先做poj3311,2进制的状态压缩dp,除了状态压缩不一样,剩下的都异曲同工。

http://blog.csdn.net/u013508213/article/details/46367815


题意:

可以任选一个城市出发,每个城市可以经过两次,要求遍历所有城市之后回到这个任选的城市,求最小的花费。


解析:

因为每个城市可以经过两次,所以对于每一个城市,可以用一个三进制数来表示其状态,0, 1, 2表示城市没有被访问过,被访问过1次,被访问过2次。

dp[ j ] [ state ] 表示 从 j 这个点出发,在state状态下,最小的花费。

状态转移方程和2进制的一样:

dp [ j ] [ state ] = min ( dp [ j ] [ state ] , dp[ k ] [ state - three[ j ] ] + g[ k ] [ j ] )

意义也是,在状态 state 下,回到 j 点没有被此次访问的状态,枚举前一点 k ,比较 经过 k 到达 j 的最短花费。


代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1

using namespace std;
const int inf = 0x3f3f3f3f;

int dp[12][59050];
int g[12][12];
int three[12];
int num[59050][12];
int n, m;

void threeTable()
{
	three[1] = 1;
	for (int i = 2; i <= 12; i++)
	{
		three[i] = three[i - 1] * 3;
	}

	for (int state = 0; state < three[11]; state++)
    {
        int t = state;
        for (int j = 1; j <= 10; j++)
        {
            num[state][j] = t % 3;
            t /= 3;
        }
    }

}

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
    #endif // LOCAL
    threeTable();
    while (~scanf("%d%d", &n, &m))
    {
        memset(dp, inf, sizeof(dp));
        memset(g, inf, sizeof(g));
        int ans = inf;
        while (m--)
        {
            int fr, to, cost;
            scanf("%d%d%d", &fr, &to, &cost);
            if (cost < g[fr][to])
                g[fr][to] = g[to][fr] = cost;
        }
        for (int i = 1; i <= n; i++)
            dp[i][three[i]] = 0;//起点
        for (int state = 0; state < three[n + 1]; state++)
        {
            bool flag = true;
            for (int j = 1; j <= n; j++)
            {
                if (num[state][j] == 0)//每个点都要被遍历过
                    flag = false;
                for (int k = 1; k <= n; k++)
                {
//                    cout << num[state][j] << endl;
                    if (num[state][j] != 0)
                    {
                        dp[j][state] = min(dp[j][state], dp[k][state - three[j]] + g[k][j]);
                    }
                }
            }
//            cout << flag << endl;
            if (flag)
            {
                for (int i = 1; i <= n; i++)
                {
                    ans = min(ans, dp[i][state]);
                }
            }
        }
        if (ans == inf)
            ans = -1;
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值