NOIP2017 luogu 3959 宝藏 - 状压dp

按照一层一层的加叶子的方式转移即可
每个叶子都贪心连向当前集合中距离最近的那一个
看上去是有问题的因为这样转移会导致叶子挂在了错误的层上,但是只会让答案错误的被计算的更大
而最优解一定会被某个转移计算到,因此是对的。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<climits>
#include<unordered_map>
#define INF (INT_MAX/2-10)
#define gc getchar()
#define N 15
#define MAXS 4200
#define rep(i,a,b) for(int i=a;i<b;i++)
#define reps(s,S) for(int s=S;s;s=(s-1)&(S))
#define Min(x,y) x=min(x,y)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int g[N][N],dp[N][MAXS],cnt[MAXS],con[MAXS][N];
unordered_map<int,int> cst[MAXS];int lb[MAXS],id[MAXS];
int main()
{
    int n=inn(),m=inn(),all=1<<n,x,y;
    rep(i,0,n) rep(j,0,n) g[i][j]=(i==j?0:INF);
    rep(i,1,all) lb[i]=i&-i;rep(i,0,n) id[1<<i]=i;
    rep(i,0,m) x=inn()-1,y=inn()-1,g[x][y]=g[y][x]=min(g[x][y],inn());
    rep(i,0,all) rep(j,0,n) cnt[i]+=(con[i][j]=((i>>j)&1));
    rep(i,0,all) reps(s,(all-1)^i) cst[i][s]=INF;
    rep(i,0,all) cst[i][0]=INF;rep(i,0,all) cst[0][i]=0;
    rep(i,0,n) rep(j,1,all) if(!con[j][i])
        cst[1<<i][j]=min(cst[1<<i][j^lb[j]],g[i][id[lb[j]]]);
    rep(i,1,all) if(cnt[i]>1) reps(s,(all-1)^i)
        Min(cst[i][s],cst[i^lb[i]][s]+cst[lb[i]][s]);
    rep(i,0,n+1) rep(s,0,all) dp[i][s]=INF;rep(i,0,n) dp[0][1<<i]=0;
    rep(i,0,n) rep(s,0,all) if(dp[i][s]<INF)
    {
        Min(dp[i+1][s],dp[i][s]);
        reps(t,(all-1)^s) if(cst[t][s]<INF) Min(dp[i+1][s|t],dp[i][s]+cst[t][s]*(i+1));
    }
    return !printf("%d\n",dp[n][all-1]);
}

猜你喜欢

转载自blog.csdn.net/mys_c_k/article/details/80798273