HDU-3001 TSP+三进制DP

题意:给出一个无向图,每个点不能被经过超过两次,选择一个起点问经过所有点至少一次的最短路径。

解法:注意此题是每个点不能经过超过两次,这和一般的TSP问题不同。但是也没有使得此题变得很复杂,原来的状态我们是用二进制压缩来表示,这时候就不够用了,我们得表示经过0次,1次,2次。想到什么了?没错就是三进制表示即可,其他和TSP问题差不多。dp[S][x]代表经过状态为S现在在x点距离完成目标的最短路径长度。照例dp即可。

#include<bits/stdc++.h>
using namespace std;
const int M=10*10;
const int INF=0x3f3f3f3f; 
int n,m;

int cnt,head[M<<1],nxt[M<<1],to[M<<1],len[M<<1];
void add_edge(int x,int y,int z) {
    nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt;
}

int inc(int S,int x) {
    int p=1; for (;x>1;x--) p*=3;
    return S+p;
}
int get(int S,int x) {
    int t=1;
    while (t<=n) {
        if (t==x) return S%3;
        S/=3; t++;
    }
}
bool check(int S) {
    int t=1;
    while (t<=n) {
        if (S%3==0) return 0;
        S/=3; t++;
    }
    return 1;
}

int dp[200000][12];
int dfs(int S,int x) {
    if (check(S)) return 0;
    if (dp[S][x]!=-1) return dp[S][x];
    int ret=INF;
    for (int i=head[x];i;i=nxt[i]) {
        int y=to[i];
        if (get(S,y)<2) ret=min(ret,dfs(inc(S,y),y)+len[i]);
    }
    return dp[S][x]=ret;
}

int main()
{
    while (scanf("%d%d",&n,&m)==2) {
        cnt=1,memset(head,0,sizeof(head));
        for (int i=1;i<=m;i++) {
            int x,y,z; scanf("%d%d%d",&x,&y,&z);
            add_edge(x,y,z);
            add_edge(y,x,z);
        }
        memset(dp,-1,sizeof(dp));
        int ans=INF;
        for (int i=1;i<=n;i++) ans=min(ans,dfs(inc(0,i),i));
        if (ans==INF) puts("-1"); else printf("%d
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/clno1/p/10943666.html