bzoj4349&2260 最小树形图(商店购物)
原题地址:
http://www.lydsy.com/JudgeOnline/problem.php?id=4349
http://www.lydsy.com/JudgeOnline/problem.php?id=2260
题意:
小C现在正要攻打科学馆腹地——计算机第三机房。而信息组的同学们已经建好了一座座堡垒,准备迎战。小C作为一种高度智慧的可怕生物,早已对同学们的信息了如指掌。
攻打每一个人的堡垒需要一个代价,而且必须攻打若干次才能把镇守之人灭得灰飞烟灭。
当小C在绞尽脑汁想攻打方案时,突然从XXX的堡垒中滚出来一个纸条:一个惊人的秘密被小C发现了:原来各个堡垒之间会相互提供援助,但是当一个堡垒被攻打时,他对所援助的堡垒的援助就会停止,因为他自己已经自身难保了。也就是说,小C只要攻打某个堡垒一次之后,某些堡垒就只需要花更小的代价攻击了。
现在,要你求消灭全机房要用掉代价最小多少。
数据范围
N<=10000,T<=7
题解:
裸题。
复杂度O(VE)
最小树形图当没有固定根时,新建虚根,向每个点连边权为inf( ∑边权+1 )的边,如果发现把inf的边计入答案了,那么无解。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=105;
const int M=20005;
const double inf=1e50;
struct node
{
int u,v; double w;
node(){}
node(int u,int v,double w):u(u),v(v),w(w){}
}E[M];
double c[N],in[N];
int n=0,m=0,k=0,cnt=0,root,vis[N],id[N],num[N],tail=1,pre[N];
double solve()
{
double ret=0;
while(1)
{
for(int i=1;i<=n;i++){vis[i]=0; id[i]=0; in[i]=inf;}
for(int i=1;i<=m;i++)
{
if(E[i].u!=E[i].v&&E[i].w<in[E[i].v])
{in[E[i].v]=E[i].w; pre[E[i].v]=E[i].u;}
}
in[root]=0;
for(int i=1;i<=n;i++) if(in[i]>=inf) return -1;
int tot=0;
for(int i=1;i<=n;i++)
{
ret+=in[i];int j;
for(j=i;vis[j]!=i&&j!=root&&!id[j];j=pre[j]) vis[j]=i;
if(j!=root&&!id[j])
{
tot++;
for(int k=j;!id[k];k=pre[k]) id[k]=tot;
}
}
if(!tot) return ret;
for(int i=1;i<=n;i++) if(!id[i]) id[i]=++tot;
for(int i=1;i<=m;i++)
{
int u=E[i].u; int v=E[i].v;
E[i].u=id[E[i].u]; E[i].v=id[E[i].v];
if(E[i].u!=E[i].v) E[i].w-=in[v];
}
n=tot; root=id[root];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
double a; int b;
scanf("%lf%d",&a,&b);
if(b) {++m; E[m]=node(0,m,a); c[m]=a; num[m]=b; id[i]=m;}
}
root=n=m+1; cnt=m;
for(int i=1;i<=cnt;i++) E[i].u=n;
scanf("%d",&k);
for(int i=1;i<=k;i++)
{
int u,v; double ww; scanf("%d%d%lf",&u,&v,&ww);
if(id[u]&&id[v])
{
E[++m]=node(id[u],id[v],ww);
c[id[v]]=min(c[id[v]],ww);
}
}
double ans=solve();
for(int i=1;i<=cnt;i++) ans+=(double)(num[i]-1)*c[i];
printf("%0.2lf\n",ans);
return 0;
}