1. 程式人生 > >【狀壓DP】【NOIP2017】寶藏

【狀壓DP】【NOIP2017】寶藏

分析:

很簡單的狀壓DP水題。。。一年前我居然不會。。。太菜了。。。

DP[i][j]表示前i層,被選中的狀態為j的最小代價。 每次列舉一個集合k,把k中每個點向j中最短的邊取和。再乘上(i+1)

這裡並不需要區分哪些是上一層的,因為優越性已經可以保證方案的正確性了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 20
#define MAXM 4100 using namespace std; typedef long long ll; ll dist[MAXN][MAXN]; ll len[MAXM][MAXM]; int bits[MAXM]; ll dp[MAXN][MAXM]; ll min1(ll x,ll y){ if(x==-1) return y; if(y==-1) return x; return min(x,y); } int main(){ int n,m,u,v,val; memset(dist,-1,sizeof
dist); SF("%d%d",&n,&m); for(int i=1;i<=m;i++){ SF("%d%d%d",&u,&v,&val); u--; v--; dist[u][v]=min1(dist[u][v],val); dist[v][u]=dist[u][v]; } for(int i=0,j=1;i<n;i++,j<<=1) bits[j]=i; for(int i=1;i<(1
<<n);i++){ int pre=((1<<n)-1)^i; for(int j=pre;j;j=(j-1)&pre){ int x=bits[i&(-i)]; ll mindist=-1; for(int s=j;s;s^=(s&(-s))){ int y=bits[s&(-s)]; mindist=min1(mindist,dist[x][y]); } if(mindist!=-1&&len[i^(1<<x)][j]!=-1) len[i][j]=len[i^(1<<x)][j]+mindist; else len[i][j]=-1; } } memset(dp,-1,sizeof dp); for(int i=0;i<n;i++) dp[0][1<<i]=0; ll ans=-1; for(int i=0;i<n;i++) for(int j=0;j<(1<<n);j++){ if(dp[i][j]==-1||(ans!=-1&&dp[i][j]>ans)) continue; int x=((1<<n)-1)^j; for(int k=x;k!=0;k=(k-1)&x) if(len[k][j]!=-1) dp[i+1][k|j]=min1(dp[i+1][k|j],dp[i][j]+(i+1)*len[k][j]); if(j==(1<<n)-1) ans=min1(ans,dp[i][j]); } PF("%lld",ans); }