HDU 3311 Dig The Wells 【斯坦納樹:狀壓DP+SPFA】
阿新 • • 發佈:2020-09-10
題意
可以在\(n+m\)個點上打井,可以修\(p\)條路,求前\(n\)個點能取到井水的最小花費是多少。
題解
把打井也轉化為修路,即在\(0\)有一口井,然後求通過修路將\(0,1,...,n\)這\(n+1\)個點連通的最小費用。
如果是換成連通所有點的話,這個題就是一個最小生成樹,可惜換不得。
然而這個部分連通的最小花費也有對應的資料結構:斯坦納樹。這個題就是斯坦納樹的模板題。
關於斯坦納樹的具體做法就不詳細說了,就是狀壓DP+SPFA。
程式碼
#include <bits/stdc++.h> #define x first #define y second #define pb push_back using namespace std; typedef long long LL; typedef pair<int, int> PII; const double PI = acos(-1.0); const double eps = 1e-8; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f; const int N=1e4+10; const int M=5e4+10; const int mod=1e9+7; inline LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } inline LL lcm(LL a, LL b) { return a * b / gcd(a, b); } inline LL read(){LL x=0,f=1;char c=getchar();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;} inline LL qpow(LL x,LL k=mod-2,LL m=mod){LL res=1;while(k){if(k&1) res=res*x%m;x=x*x%m;k>>=1;}return res;} /*=================================================================================================================*/ int n,m,p,state[N]; int head[N],to[M*2],nxt[M*2],val[M*2],tot; LL f[1010][100]; void add(int u,int v,int w){ to[++tot]=v;nxt[tot]=head[u];val[tot]=w;head[u]=tot; } queue<PII> que; int vis[1010][100]; void spfa(){ while(!que.empty()){ int u=que.front().x,sta=que.front().y; vis[u][sta]=0;que.pop(); for(int i=head[u];i;i=nxt[i]){ int vsta=sta|state[to[i]]; if(f[to[i]][vsta]>f[u][sta]+val[i]){ f[to[i]][vsta]=f[u][sta]+val[i]; if(!vis[to[i]][vsta]&&sta==vsta){ vis[to[i]][vsta]=1; que.push({to[i],vsta}); } } } } } void Solve(){ for(int i=0;i<=n+m;i++) head[i]=0; tot=0; for(int i=1,x;i<=n+m;i++){ scanf("%d",&x); add(0,i,x);add(i,0,x); } for(int i=1,u,v,w;i<=p;i++){ scanf("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w); } for(int i=0;i<=n+m;i++)for(int j=0;j<(1<<n+1);j++)f[i][j]=INF; for(int i=0;i<=n;i++)f[i][state[i]=1<<i]=0; for(int i=n+1;i<=m;i++) state[i]=0; for(int sta=0;sta<(1<<n+1);sta++){ for(int i=0;i<=n+m;i++){ //列舉子集 for(int sub=sta&(sta-1);sub;sub=(sub-1)&sta) f[i][sta]=min(f[i][sta],f[i][sub|state[i]]+f[i][sta-sub|state[i]]); if(f[i][sta]!=INF){ que.push({i,sta}); vis[i][sta]=1; } } spfa(); } LL ans=INF; for(int i=0;i<=n+m;i++) ans=min(ans,f[i][(1<<n+1)-1]); printf("%lld\n",ans); } int main(){ while(~scanf("%d%d%d",&n,&m,&p)) Solve(); return 0; }