CF 335 div.1-B/div.2-D/605 B Lazy Student

题目链接:http://codeforces.com/problemset/problem/605/B

题目大意:有一个网(加权图),无向,无环,给出你他的顶点个数n(2<=n<=100000)和边的个数m( 1<=m<=100000且n-1<=m<=n*(n-1)/2 ),然后有m行,每行是一个整数和一个bool变量,第一个数表示这个边的权值,第二个数,1表示这条边在这个图的最小生成树(MST)里,0表示不在MST里。求满足条件的每个边连接的两个顶点序号,有多种情况,给出其中一种即可,如果不存在输出-1,存在就输出m行,每行两个数表示这条边所连接的两个顶点(输出顺序要与输入顺序对应)。

解:都有点不太想写,这套题都是构造题0.0,没有完全的构造出来,最后还是看的题解。

假设有两个顶点集U和V,初始时,U中只有1号顶点,V中有其他的顶点。

先将所有的边按升序排序,权值一样的话将属于MST的排到前面,然后从小到大处理每条边。

遇到一个新的属于MST的边就把这条边连接一个V中的顶点,并把这个顶点从V中移除,加到U中,如果是不属于MST的,就让这个边连接U中任意两个未连接的顶点。不合法的情况就是在连接U中任意两个未连接的顶点时没有点可连。

两种构造方法。一,对于MST的里的边就是1-2,1-3,,,1-n,

对于不是MST里的边,假设当前U里有x个顶点,设l和r是连接这条边连接的两个点,那么每次,如果l<r,就作连接,然后l++,否则就是l==r,那就让r++,l=2。非法情况就是r++时,如果r>x,就是非法,输出-1.

二,对于MST里的边就是1-2,2-3,,,(n-1)-n。

对于不是MST里的边,假设当前U里有x个顶点,设l和r是连接这条边连接的两个点,那么每次,如果l < r-1,就作连接,然后l++,否则就是l==r-1,那就让r++,l=1。非法情况就是r++时,如果r>x,就是非法,输出-1.

其实就是构造,反正构造出一种合理的方法就好。


代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m;
//bool U[100005], V[100005];
int connect[100005][2];
struct edge_unit
{
	int k, i, f;
	bool operator <(edge_unit a)
	{
		if (this->k < a.k)
			return true;
		else if (this->k == a.k)
			return this->f>a.f;
		else
			return false;
	}
}edge[100005];
bool work()
{
	int l = 2, r = 2, p = 1;
	for (int i = 0; i < m; ++i)
	{
		if (edge[i].f)
		{
			connect[edge[i].i][0] = 1;
			connect[edge[i].i][1] = ++p;
		}
		else
		{
			if (l == r)
			{
				++r; l = 2;
				if (r > p)
					return 0;
			}
			connect[edge[i].i][0] = l++;
			connect[edge[i].i][1] = r;
		}
	}
	return 1;
}
int main()
{
	//freopen("input.txt", "r", stdin);
	scanf("%d%d", &n, &m);
	int x, flag;
	for (int i = 0; i < m; ++i)
	{
		scanf("%d%d", &x, &flag);
		edge[i].k = x;
		edge[i].f = flag;
		edge[i].i = i;
	}
	sort(edge, edge + m);
	if (work())
		for (int i = 0; i < m; ++i)
			printf("%d %d\n", connect[i][0], connect[i][1]);
	else
		printf("-1\n");
	//system("pause");
	//while (1);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值