题意
可以在\(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;
}