HDU 5418 Victor and World (状态压缩dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5418

题目大意:有n个结点m条边(有边权)组成的一张连通图(n <16, m<100000)。求从第一个点出发,经过每个点至少一次后回到原点的最小路径边权和。

分析:发现我还真是菜。

  n<16,很明显的状态压缩标记,先将所有点的编号减去1,使其从0开始编号。dp[i][j]表示从0号点出发,当前状态为i (二进制位为1表示对应点已走过,否则没走过), 当前位置为 j,  回到原点的最小代价, 则dp[(1<<n)-1][0]为所求解,用类似 spfa 的方法可以更新到所有情况。

参考代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

int dp[1<<17][17];
int mp[17][17];
int dis[17];
bool vis[1<<17][17];
int n;

int main()
{
    int T, m;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &m);
        memset(mp, 0x7f, sizeof(mp));
        for(int i = 0; i < n; i++) mp[i][i] = 0;
        for(int i = 0; i < m; i++) {
            int u, v, d;
            scanf("%d %d %d", &u, &v, &d);
            u--; v--;
            mp[u][v] = mp[v][u] = min(mp[u][v], d);
        }
        memset(dp, 0x7f, sizeof(dp));
        memset(vis, 0, sizeof(vis));
        dp[0][0] = 0, vis[0][0] = 1;
        queue<pair<int, int> > q;
        q.push(make_pair(0,0));
        while(!q.empty())
        {
            int s = q.front().first;
            int u = q.front().second;
            q.pop();
            for(int i = 0; i < n; i++)
            {
                int ss = s | (1<<i);
                if(dp[ss][i] > dp[s][u] + mp[u][i]){
                    dp[ss][i] = dp[s][u] + mp[u][i];
                    if(vis[ss][i] == 0){
                        vis[ss][i] = 1;
                        q.push(make_pair(ss, i));
                    }
                }
            }
        }
        //for(int i = 0; i < (1<<n); i++) for(int j = 0; j < n; j++) printf("%d %d: %d\n", i, j, dp[i][j]);
        printf("%d\n", dp[(1<<n)-1][0]);
    }
    return 0;
}

 

posted @ 2015-09-18 21:14  beisong  阅读(321)  评论(1编辑  收藏  举报