洛谷P3104题解

洛谷P3104题解

Farmer John’s N cows (2 <= N <= 500) have joined the social network “MooBook”.

Each cow has one or more friends with whom they interact on MooBook. Just for fun, Farmer John makes a list of the number of friends for each of his cows, but during the process of writing the list he becomes distracted, and he includes one extra number by mistake (so his list contains N+1 numbers, instead of N numbers as he intended).

Please help Farmer John figure out which numbers on his list could have been the erroneous extra number.

这道题的标签虽然是生成树,但是和生成树应该来说没有任何关系,因此这道题也提醒了我不要太相信标签。

我一开始的想法是枚举每个数把它删掉,然后判断剩下的合不合法,其实这个思路的确是正解,但关键就在于怎么判断合不合法,正确做法是:贪心。贪心的策略是:我们按朋友数对奶牛进行排序,从大到小进行配对。配对完毕后,假如第一头奶牛的朋友数不为 0 0 0,那说明这种方案是不合法的;否则,这 n n n个数就会被分成三部分:

  1. 第一头奶牛,也就是朋友数最多的奶牛
  2. 2 ∼ p − 1 2 \sim p-1 2p1头奶牛,其中 p p p满足的条件是第一头奶牛匹配到第 p p p头奶牛时朋友数变为 0 0 0(被用于匹配的奶牛)
  3. p ∼ n p \sim n pn头奶牛(未被用于匹配的奶牛)

然后我们要重复上述的步骤,就需要对剩下奶牛的朋友数再次进行排序,但假如我们用 O ( n l o g n ) O(nlogn) O(nlogn)的快排,对于这道题来说是会 T T T的。这里有一个不易觉察的性质, 2 ∼ p − 1 2 \sim p-1 2p1奶牛的朋友数和 p ∼ n p \sim n pn奶牛的朋友数是分别单调减的,因此我们可以用归并排序的merge方法进行排序,只需要 O ( n ) O(n) O(n)。最终循环终止的条件是排序后的第一头奶牛朋友数为 0 0 0,这样我们得到的是一种合法的方案。

总结一下这道的做法:首先枚举删去哪个数,然后对剩下的 n n n头奶牛迭代重复我们的贪心策略,假如最终能够成功地将所有奶牛的朋友数变为 0 0 0(也就是成功匹配),那么就记录这种方案;反之,如果出现第一头奶牛与剩余奶牛都匹配完一遍还有朋友数的情况,那就是不合法的方案。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000;
int n, ans, org[N], del[N], temp[N], res[N];

bool cmp(int x, int y) {
	return x > y;
}

void work() {
	for(int i = 1; i <= n + 1; ++i) {
		int tot_deg = 0; 
		int idx = 0;
		for(int j = 1; j <= n + 1; ++j) {
			if(j != i) {
				tot_deg += org[j];
				del[++idx] = org[j];
			}
		}
		if(tot_deg % 2 != 0) continue;

		sort(del + 1, del + n + 1, cmp);
		
		bool flag = 1;
		for(int j = 1; j <= n; ++j) {
			if(del[1] == 0) {
				break;
			}
			int p = 2;
			while(del[1] > 0 && del[p] > 0) {
				del[1]--;
				del[p]--;
				p++;
			}
			if(del[1] > 0) {
				flag = 0;
				break;
			}

			int l = 2, r = p, cnt = 0;
			while(l < p && r <= n) {
				if(del[l] >= del[r]) {
					temp[++cnt] = del[l];
					l++;
				}
				else {
					temp[++cnt] = del[r];
					r++;
				}
			}
			while(l < p) {
				temp[++cnt] = del[l];
				l++;
			}
			while(r <= n) {
				temp[++cnt] = del[r];
				r++;
			}
			
			for(int i = 1; i <= cnt; ++i) {
				del[i] = temp[i];
			}
			del[n] = 0;
		}
		if(flag) {
			res[++ans] = i;
		}
	}
}
int main()
{
	cin>>n;
	for(int i = 1; i <= n + 1; ++i) {
		cin>>org[i];
	}
	work();
	cout<<ans<<endl;
	for(int i = 1; i <= ans; ++i) {
		cout<<res[i]<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值