HDU 3311 Dig The Wells(斯坦纳树)

时间:2023-03-09 06:44:57
HDU 3311 Dig The Wells(斯坦纳树)

【题目链接】

http://acm.hdu.edu.cn/showproblem.php?pid=3311

【题意】

给定k座庙,n个其他点,m条边,点权代表挖井费用,边权代表连边费用,问使得k座庙里的所有和尚都能吃到水的最小费用。

【思路】

首先一个相连的块里只要有口井就能保证块里的和尚有水。所以这个题目标并不是要让k个点连通,但我们可以转化一下。

在原图的基础上我们添加0号结点,由0号结点向所有的点连边为该点的点权,代表挖井的费用,从而保证每个块里都有井,则问题转化为求以0为根包含k个点的斯坦纳树。

  模板可以上了。

奇技淫巧系列。。。

【代码】

 #include<set>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std; typedef long long ll;
const int N = 1e3+;
const int M = 2e4+;
const int inf = 0xf0f0f0f;
const int P = ; ll read() {
char c=getchar();
ll f=,x=;
while(!isdigit(c)) {
if(c=='-') f=-; c=getchar();
}
while(isdigit(c))
x=x*+c-'',c=getchar();
return x*f;
}
struct Edge { int v,w,nxt;
}e[M];
int en=,front[N];
void adde(int u,int v,int w)
{
e[++en]=(Edge){v,w,front[u]}; front[u]=en;
} int n,m,K;
int f[<<P][N];
queue<int> q; int inq[N]; void spfa(int* dis)
{
while(!q.empty()) {
int u=q.front(); q.pop();
inq[u]=;
trav(u,i) {
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w) {
dis[v]=dis[u]+e[i].w;
if(!inq[v])
inq[v]=,q.push(v);
}
}
}
} int main()
{
while(scanf("%d%d%d",&K,&n,&m)==) {
en=;
memset(front,,sizeof(front));
int rt=;
n+=K;
FOR(i,,n) {
int w=read();
adde(rt,i,w),adde(i,rt,w);
}
FOR(i,,m) {
int u=read(),v=read(),w=read();
adde(u,v,w),adde(v,u,w);
}
memset(f,0xf,sizeof(f));
FOR(i,,K) f[<<i][i]=;
int all=<<(K+);
FOR(st,,all-) {
FOR(i,,n) {
for(int s=st&(st-);s;s=(s-)&st)
f[st][i]=min(f[st][i],f[s][i]+f[st^s][i]);
if(f[st][i]!=inf) q.push(i),inq[i]=;
}
spfa(f[st]);
}
printf("%d\n",f[all-][rt]);
}
return ;
}