简介:
就是个状压dp起了个高端的名字。
例题:
给定一张图,其中有p个关键点,连接每个点有一个代价,要求用最少的代价连通所有关键点。
$nleq 100,pleq 10$。
做法:
发现最小生成树并不能做,虚树也不能做,实际上这个问题就没有多项式复杂度的做法。
于是考虑状压dp。设$dp(i,j)$表示当前位于点i(不一定是关键点),与关键点的连通状态为j的最小代价。
转移有两种,一种是合并当前点的两个状态,一种是同样的连通状态从一个点转移到另一个点。
具体地:
- $dp(i,j)=min { dp(i,j),dp(i,k)+dp(i,j-k)-val_i }$,表示合并点i的两个状态。
- $dp(i,j)=min { dp(i,j),dp(k,j)+val_i }$,表示点k的状态转移到点i。
由于每个状态对应的连通块肯定是一棵树,所以位于点i就相当于以i为根,两种转移也相当于合并子树和添加新根的操作。
第二种转移可以用spfa优化,总复杂度$O(n imes 3^{p})$。
代码([WC2008]游览计划):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define maxn 10 #define maxm 505 #define inf 0x7fffffff #define ll long long #define pii pair<int,int> #define mp make_pair #define fi first #define se second #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int n,m,A[maxm],dp[maxm][1<<maxn],vis[maxm]; int hd[maxm],nxt[maxm],cst[maxm],cnt; int inq[maxm],to[maxm],rk[maxm]; pii fr[maxm][1<<maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline int id(int x,int y){return (x-1)*m+y;} inline void addedge(int u,int v,int w){to[++cnt]=v,cst[cnt]=w,nxt[cnt]=hd[u],hd[u]=cnt;} inline void spfa(int s){ memset(inq,0,sizeof(inq)); queue<int> q; for(int i=1;i<=n*m;i++) if(dp[i][s]<=1e9) q.push(i),inq[i]=1; while(!q.empty()){ int u=q.front(); q.pop(),inq[u]=0; for(int i=hd[u];i;i=nxt[i]){ int v=to[i],w=cst[i]; if(dp[v][s]>dp[u][s]+w){ dp[v][s]=dp[u][s]+w,fr[v][s]=mp(u,s); if(!inq[v]) q.push(v),inq[v]=1; } } } } inline void dfs(int i,int j){ vis[i]=1; if(!fr[i][j].fi) return; dfs(fr[i][j].fi,fr[i][j].se); if(fr[i][j].fi==i) dfs(fr[i][j].fi,j^fr[i][j].se); } int main(){ n=read(),m=read(); memset(dp,63,sizeof(dp)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ int w=read(); A[id(i,j)]=w; if(j>1) addedge(id(i,j-1),id(i,j),w); if(i>1) addedge(id(i-1,j),id(i,j),w); if(j<m) addedge(id(i,j+1),id(i,j),w); if(i<n) addedge(id(i+1,j),id(i,j),w); if(w==0) rk[++rk[0]]=id(i,j),dp[id(i,j)][1<<(rk[0]-1)]=0; } for(int j=0;j<(1<<rk[0]);j++){ for(int i=1;i<=n*m;i++) for(int k=j;k;k=(k-1)&j) if(dp[i][j]>dp[i][j^k]+dp[i][k]-A[i]) dp[i][j]=dp[i][j^k]+dp[i][k]-A[i],fr[i][j]=mp(i,k); spfa(j); } int ans=inf,res=0; for(int i=1;i<=n*m;i++) if(dp[i][(1<<rk[0])-1]<ans) ans=dp[i][(1<<rk[0])-1],res=i; printf("%d ",ans); dfs(res,(1<<rk[0])-1); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(A[id(i,j)]==0) printf("x"); else if(vis[id(i,j)]) printf("o"); else printf("_"); } printf(" "); } return 0; }