4349: 最小树形图
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 379 Solved: 167
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
10.00 1
1.80 1
2.50 2
2
1 3 2.00
3 2 1.50
Sample Output
HINT
Source
最小树形图模板题,下面介绍求最小树形图的步骤:(概念不再赘述,不懂自行百度)
1、找到除了root以为其他点的权值最小的入边。用In[i]记录
2、如果出现除了root以为存在其他孤立的点,则不存在最小树形图。
3、找到图中所有的环,并对环进行缩点,重新编号。
4、更新其他点到环上的点的距离,如:
环中的点有(Vk1,Vk2,… ,Vki)总共i个,用缩成的点叫Vk替代,则在压缩后的图中,其他所有不在环中点v到Vk的距离定义如下:gh[v][Vk]=min { gh[v][Vkj]-mincost[Vkj] } (1<=j<=i)
而Vk到v的距离为 gh[Vk][v]=min { gh[Vkj][v] } (1<=j<=i)
5、重复3,4知道没有环为止。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const double inf=1e10;
#define maxn 1010
#define maxm 40010
/*
* 最小树形图
* int型
* 复杂度O(NM)
* 点总0开始
*/
struct Edge
{
int u,v;
double cost;
};
Edge edge[maxm];
int pre[maxn],id[maxn],vis[maxn];
double in[maxn];
double zhuliu(int root,int n,int m,Edge edge[])
{
int u,v,i;
double res=0;
while(1)
{
for(i=0;i<n;i++)
in[i]=inf;
for(i=0;i<m;i++)
if(edge[i].u!=edge[i].v && edge[i].cost<in[edge[i].v])
{
pre[edge[i].v]=edge[i].u;
in[edge[i].v]=edge[i].cost;
}
/* for(i=0;i<n;i++)
if(i!=root && in[i]==inf)
return -1;//不存在最小树形图*/
int tn=0;//记录有向环的数量
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[root]=0;
for(i=0;i<n;i++)
{
res+=in[i];
v=i;
while(vis[v]!=i && id[v]==-1 && v!=root)
vis[v]=i,v=pre[v];
if(v!=root && id[v]==-1)
{
for(u=pre[v];u!=v;u=pre[u])
id[u]=tn;
id[v]=tn++;
}
}
if(tn==0)//当前图中不再存在环了
break;
for(i=0;i<n;i++)
if(id[i]==-1)
id[i]=tn++;
for(i=0;i<m;)
{
v=edge[i].v;
edge[i].u=id[edge[i].u];
edge[i].v=id[edge[i].v];
if(edge[i].u!=edge[i].v)
edge[i++].cost-=in[v];
else
swap(edge[i],edge[--m]);
}
n=tn;
root=id[root];
}
return res;
}
int num[maxn],flag[maxn];
double a[maxn];
int main(void)
{
double v,ans;
int n,m,i,j,x,y,m1;
while(scanf("%d",&n)!=EOF)
{
ans=0;int cnt=0;m=0;
memset(flag,0,sizeof(flag));
for(i=0;i<n;i++)
{
scanf("%lf%d",&v,&x);
if(x==0)
continue;
flag[i]=++cnt;
edge[m].u=0;
edge[m].v=cnt;
edge[m++].cost=v;
a[cnt]=v;num[cnt]=x-1;
}
n=cnt+1;
scanf("%d",&m1);
for(i=1;i<=m1;i++)
{
scanf("%d%d%lf",&x,&y,&v);
x--;y--;
if(flag[x]==0 || flag[y]==0)
continue;
edge[m].u=flag[x];
edge[m].v=flag[y];
edge[m++].cost=v;
a[flag[y]]=min(a[flag[y]],v);
}
for(i=1;i<=cnt;i++)
ans+=a[i]*num[i];
printf("%.2f\n",ans+zhuliu(0,n,m,edge));
}
return 0;
}
/*
3
10.00 1
1.80 1
2.50 2
2
1 3 2.00
3 2 1.50
*/