POJ 2404 Jogging Trails [DP 状压 一般图最小权完美匹配]

传送门

题意:找一个经过所有边权值最小的回路,$n \le 15$


 

所有点度数为偶则存在欧拉回路,直接输出权值和

否则考虑度数为奇的点,连着奇数条边,奇点之间走已经走过的路移动再走没走过的路

然后大体想一想就是权值和加上奇点的最小权匹配啦

蒟蒻不会带花树就打了状压$DP$

$f[s]$表示已经选的集合为$s$,考虑当前未选的最小点和哪一个未选的点匹配

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=18,S=(1<<15)+5,INF=1e9;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int n,m,u,v,w,d[N][N],de[N];
void floyd(){
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++) if(d[i][k]<INF)
            for(int j=1;j<=n;j++) 
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
int a[N],p,f[S];
void dp(){
    //for(int i=1;i<=p;i++) printf("a %d\n",a[i]);
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    int All=1<<p;
    for(int s=0;s<All;s++){
        int i=0;
        while((1<<i)&s) i++;
        for(int j=i+1;j<p;j++) if( !((1<<j)&s) )
            f[s|(1<<i)|(1<<j)]=min(f[s|(1<<i)|(1<<j)],f[s]+d[a[i]][a[j]]);
    }
}
int main(){
    freopen("in","r",stdin);
    while( (n=read()) ){
        m=read();
        for(int i=1;i<=n;i++){
            de[i]=0;
            for(int j=1;j<=n;j++) if(i!=j) d[i][j]=INF;
        }
        int sum=0;
        for(int i=1;i<=m;i++){
            u=read(),v=read(),w=read();
            d[u][v]=d[v][u]=min(d[u][v],w); sum+=w;
            de[u]++;de[v]++;
        }
        p=0;
        for(int i=1;i<=n;i++) if(de[i]&1) {a[p++]=i;}
        if(!p) {printf("%d\n",sum);continue;}

        floyd();
        dp();
        printf("%d\n",sum+f[(1<<p)-1]);
    }
}

 

posted @ 2017-03-07 20:57  Candy?  阅读(402)  评论(0编辑  收藏  举报