hdu2121

/*
分析:
    无定根的最小树形图。
    虚拟出一个root,到每个node都有一条边,其权值>整张map中的边的权值和。
    至于这个题,当最小树形图的权值相等时,要选择node编号最小的city作为中心这
点,代码行45可以得到所需index,因为在找最小的in的时候,我们是按照边的编号从小
到大遍历的,且在加入虚拟root到city的边的时候,也是按照city编号的升序加的,所
以碰到的第一个满足:“出发点是u、且其指向的city没有被缩掉” 的边,其指向的city
一定为所求。

                                                             2013-04-26
*/





#include"iostream"
#include"cstdio"
#include"cstring"
using namespace std;
const int N=1010;
const int M=10011;
const int inf=INT_MAX;

int n,m,root,ans_index;
__int64 sum,in[N];
int id[N],pre[N],vis[N];
struct Edge{
	int f,t;
	__int64 w;
}edge[M];
int tot;
void add(int a,int b,__int64 c){
	edge[tot].f=a;edge[tot].t=b;edge[tot++].w=c;
}
void build_map()
{
	int i,a,b,c;
	sum=tot=0;
	for(i=0;i<m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		sum+=c;add(a,b,c);
	}
	sum++;
	for(i=0;i<n;i++)	add(n,i,sum);
}
__int64 solve()
{
	int i,u,v,nv=n+1,cnt;
	__int64 ans=0;
	while(1)
	{
		//找最小in
		for(i=0;i<nv;i++)	{in[i]=inf;id[i]=-1;vis[i]=-1;}
		for(i=0;i<tot;i++)
		{
			u=edge[i].f;
			v=edge[i].t;
			if(in[v]<=edge[i].w || u==v)	continue;
			if(u==root)	ans_index=i;
			in[v]=edge[i].w;
			pre[v]=u;
		}
		in[root]=0;
		pre[root]=root;
		//判断是否中断 && 更新ans (这个题也一样,坑定的不会中断,因为虚拟的root到每个city都有边)
		for(i=0;i<nv;i++)
		{
			ans+=in[i];
			if(in[i]==inf)	return -1;
		}
		//重新构图
		cnt=0;
		for(i=0;i<nv;i++)
		{
			v=i;
			while(vis[v]==-1)
			{
				vis[v]=i;
				v=pre[v];
			}
			if(vis[v]!=i || v==root)	continue;
			for(u=pre[v];u!=v;u=pre[u])	id[u]=cnt;
			id[v]=cnt++;
		}
		if(!cnt)	break;
		for(i=0;i<nv;i++)	if(id[i]==-1)	id[i]=cnt++;
		//更新边
		for(i=0;i<tot;i++)
		{
			u=edge[i].f;
			v=edge[i].t;
			edge[i].f=id[u];
			edge[i].t=id[v];
			edge[i].w-=in[v];
		}
		//next
		nv=cnt;
		root=id[root];
	}
	return ans;
}
int main()
{
	while(scanf("%d%d",&n,&m)!=-1)
	{
		build_map();
		root=n;
		__int64 ans=solve();
		if(ans==-1 || ans-sum>=sum)	printf("impossible\n");
		else	printf("%I64d %d\n",ans-sum,ans_index-m);
		printf("\n");
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值