题意:
给定一张n个点m条边的无向图,有p个关键点,分成了c类。
连通每条边有一个代价$w_i$,求最小代价使得同一类的关键点都联通。
$nleq 1000,pleq 10$。
题解:
如果直接跑斯坦纳树会强行把所有关键点联通,但实际上两类关键点不一定非要联通。
相当于我们求了一棵斯坦纳树,但要求的是斯坦纳森林。
考虑森林中的每棵树,容易发现它们必须是若干类关键点集的并。
换句话说,设一棵树的连通状态为S,对于第i类的关键点集$T_i$,要么$T_i subseteq S$,要么$T_i cap S=emptyset$。
(我一开始只判断了前半部分居然拿到了85分,数据属实np)
于是将所有满足要求的状态S拿出来重新dp即可。
复杂度$O(n imes 3^p )$。
套路:
- 求斯坦纳森林$ ightarrow$对斯坦纳树再dp一遍。
- 推条件时:注意充分性和必要性。
代码:
#include<bits/stdc++.h> #define maxn 1005 #define maxm 10005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int hd[maxn],to[maxm],nxt[maxm],cst[maxm]; int n,m,p,dp[maxn][1<<10],inq[maxn],cnt; int sta[1<<10],res[maxn]; queue<int> q; 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 void addedge(int u,int v,int w){ to[++cnt]=v,cst[cnt]=w,nxt[cnt]=hd[u],hd[u]=cnt; to[++cnt]=u,cst[cnt]=w,nxt[cnt]=hd[v],hd[v]=cnt; } inline void spfa(int s){ for(int i=1;i<=n;i++) if(dp[i][s]<=1e9) q.push(i),inq[i]=1; while(!q.empty()){ int u=q.front(); inq[u]=0,q.pop(); 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; if(!inq[v]) q.push(v),inq[v]=1; } } } } int main(){ n=read(),m=read(),p=read(); memset(dp,63,sizeof(dp)); for(int i=1;i<=m;i++){ int u=read(),v=read(),w=read(); addedge(u,v,w); } for(int i=1;i<=p;i++){ int c=read(),d=read(); res[c]|=(1<<i-1),dp[d][1<<(i-1)]=0; } for(int j=0;j<(1<<p);j++){ for(int i=1;i<=n;i++) for(int k=j;k;k=(k-1)&j) dp[i][j]=min(dp[i][j],dp[i][k]+dp[i][j-k]); spfa(j); } memset(sta,63,sizeof(sta)); for(int j=1;j<(1<<p);j++){ bool flag=1; for(int i=1;i<=p;i++){ if(!res[i]) continue; if((j&res[i])!=res[i] && (j&res[i])!=0) flag=0; } if(!flag) continue; for(int i=1;i<=n;i++) sta[j]=min(sta[j],dp[i][j]); } for(int i=0;i<(1<<p);i++) for(int j=0;j<(1<<p);j++) sta[i|j]=min(sta[i|j],sta[i]+sta[j]); printf("%d ",sta[(1<<p)-1]); return 0; }