Command Network(POJ 3164)---定根最小树形图模板题

题目链接

题目描述

After a long lasting war on words, a war on arms finally breaks out between littleken’s and KnuthOcean’s kingdoms. A sudden and violent assault by KnuthOcean’s force has rendered a total failure of littleken’s command network. A provisional network must be built immediately. littleken orders snoopy to take charge of the project.
With the situation studied to every detail, snoopy believes that the most urgent point is to enable littenken’s commands to reach every disconnected node in the destroyed network and decides on a plan to build a unidirectional communication network. The nodes are distributed on a plane. If littleken’s commands are to be able to be delivered directly from a node A to another node B, a wire will have to be built along the straight line segment connecting the two nodes. Since it’s in wartime, not between all pairs of nodes can wires be built. snoopy wants the plan to require the shortest total length of wires so that the construction can be done very soon.

输入格式

The input contains several test cases. Each test case starts with a line containing two integer N (N ≤ 100), the number of nodes in the destroyed network, and M (M ≤ 104), the number of pairs of nodes between which a wire can be built. The next N lines each contain an ordered pair xi and yi, giving the Cartesian coordinates of the nodes. Then follow M lines each containing two integers i and j between 1 and N (inclusive) meaning a wire can be built between node i and node j for unidirectional command delivery from the former to the latter. littleken’s headquarter is always located at node 1. Process to end of file.

输出格式

For each test case, output exactly one line containing the shortest total length of wires to two digits past the decimal point. In the cases that such a network does not exist, just output ‘poor snoopy’.

输入样例

4 6
0 6
4 6
0 0
7 20
1 2
1 3
2 3
3 4
3 1
3 2
4 3
0 0
1 0
0 1
1 2
1 3
4 1
2 3

输出样例

31.19
poor snoopy

分析

题目大意是给定n个点的坐标,以及m条有向边,每条边的权值为两点的间距,求能否以1号点为根节点形成一颗最小树形图,如果可以输出这颗树的权值,否则输出“poor snoopy”。

典型的最小树形图模板题,正解是通过朱刘算法,对于朱刘算法不了解的可以看看这篇博客,主要的算法流程是这样的:

  1. 求各点最小入边权值in[i]。
  2. 判断除根节点外是否所有点都有入边,有则继续3,无则无法建树形图,输出“poor snoopy”。
  3. 缩点并统计环,如果有环就继续4,无环则表示已找到最小树形图,输出其权值即可。
  4. 建新图,对于边<u,v>,如果点u和v不在一个环路呢,则边<u,v>的权值减小in[v]。
  5. 继续从1-4,直到图中无环。

以下是源代码。

源程序

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define MAXN 105
#define MAXM MAXN*MAXN
#define INF 999999999
using namespace std;
struct Pos{
	double x,y;
}pos[MAXN];
struct Edge{
	int u,v;
	double w;
}edge[MAXM];
int id[MAXN],pre[MAXN];
double dis[MAXN];	//最小入边 
bool vis[MAXN];
double zhuliu(int root,int n,int m)
{
	double res=0;
	while(1){
		for(int i=1;i<=n;i++)	//初始化
			dis[i]=INF;
		for(int i=1;i<=m;i++){	//计算各点最小入边与前驱节点 
			int u=edge[i].u,v=edge[i].v;
			if(edge[i].w<dis[v]&&u!=v){
				pre[v]=u;
				dis[v]=edge[i].w;
			}
		} 
		dis[root]=0;
		for(int i=1;i<=n;i++)	//判断能否成树 
			if(int(dis[i])==INF)
				return -1;
		int cnt=0;	//记录环数 
		memset(id,-1,sizeof(id));
		memset(vis,false,sizeof(vis));
		for(int i=1;i<=n;i++){	//标记每个环 
			res+=dis[i];	//记录权值 
			int v=i;
			while(vis[v]!=i&&id[v]==-1&&v!=root){	//寻找有向环
				//三种情况终止:找到相同标记、节点已属其他环、遍历到根
				vis[v]=i;
				v=pre[v]; 
			}
			if(v!=root&&id[v]==-1){	//找到环
				id[v]=++cnt;
				for(int u=pre[v];u!=v;u=pre[u])
					id[u]=cnt;
			}
		}
		if(cnt==0)	//无环
			break;
		for(int i=1;i<=n;i++)	//对于非环中点
			if(id[i]==-1)
				id[i]=++cnt;
		for(int i=1;i<=m;i++){	//建新图,缩点重新标记
			int u=edge[i].u,v=edge[i].v;
			edge[i].u=id[u];
			edge[i].v=id[v];
			if(id[u]!=id[v])	//不在一个环内
				edge[i].w-=dis[v]; 
		} 
		n=cnt;
		root=id[root]; 
	}
	return res;
}
double getdis(Pos a,Pos b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main()
{
	int n,m;
	while(~scanf("%d%d",&n,&m)){
		for(int i=1;i<=n;i++)	//读入坐标 
			scanf("%lf%lf",&pos[i].x,&pos[i].y);
		for(int i=1;i<=m;i++){	//建边 
			scanf("%d%d",&edge[i].u,&edge[i].v);
			if(edge[i].u!=edge[i].v)
				edge[i].w=getdis(pos[edge[i].u],pos[edge[i].v]);
			else
				edge[i].w=INF;
		}
		double res=zhuliu(1,n,m);
		if(int(res)==-1)
            printf("poor snoopy\n");
        else
            printf("%.2f\n",res);
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值