Vijos 奖金 题解

CSDN同步

原题链接

简要题意:

已知每个员工的奖金至少是 \(100\) 元,已知若干组关系 \(x\)\(y\),必须满足 \(x\) 的奖金比 \(y\) 多(至少 \(1\) 元)。求满足所有关系的最少需要发放的奖金。(无解输出 Poor Xed

首先,我们考虑,什么情况是无解的?

如果你想不到,那么给出这样 \(3\) 种关系你看看:

1 2
2 3
3 1

\(1\)\(2\) 多,\(2\)\(3\) 多,\(3\)\(1\) 多,这不可能满足。

所以,如果 反向建图(即 \(x\)\(y\) 有边当且仅当 \(x\) 的奖金 \(< y\) 的奖金) 的话,出现环即说明无解。

那么,剩下的情况怎么计算呢?

你会说,显然啊,剩下是 有向无环图,即 \(\text{Dag}\),可以用拓扑排序的方式。

没错,思路是这样的,正解也是这样的,但是有很多细节。

下面给出一些特殊例子,用 \(f_i\) 表示 \(i\) 号节点的奖金。

1 2
1 3

如果你建的是 无向图 的话,从 \(2\) 开始搜,那么 \(f_2=100 , f_1 =101,f_3=102\),然后 关系乱伦

所以我们应当建有向图。

如果,你从任意一个入度为 \(0\) 的点开始搜并统计路径上答案,那么反例仍然这组:

1 2
1 3

你在搜 \(2,3\) 的时候把 \(1\) 重复加了。

那你说,我可以用哈希啊。

没错,这就是正解,用 \(\texttt{bfs}\),即 宽度优先搜索 实现本题,具体细节见代码。

时间复杂度:\(O(n+m)\).

实际得分:\(100pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+1;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int n,m,du[N],ans;
int sum=0,num=0;
vector<int> G[N];
bool vis[N];
queue<int> q;

int main(){
//	freopen("reward.in","r",stdin);
//	freopen("reward.out","w",stdout);
	n=read(),m=read();
	while(m--) {
		int x=read(),y=read();
		du[x]++; G[y].push_back(x); //记录入度,建图
	} goto fin;
	fin: { //利用 goto 的特性完成 while 循环,炫一波特技
		bool f=0;
		for(int i=1;i<=n;i++) 
			if(!du[i] && !vis[i]) {
				q.push(i);
				f=1; vis[i]=1;
				ans+=100;
			} //把入度为 0 且没有访问过的点入队,统计
		if(!f) goto last; //直接跳出
		while(!q.empty()) {
			int x=q.front(); q.pop();
			num++; ans+=sum; vis[x]=1;
			//num 统计人数,ans 是奖金总数
			for(int i=0;i<G[x].size();i++) du[G[x][i]]--; //删边并更新入度
		} sum++; //sum 表示当前人的奖金,每走一步增大一个
		goto fin; //自己 goto 自己,重新跳回 fin,实现 while
	} last: { //goto 就是一波神奇炫技操作
		if(num==n) printf("%d\n",ans);
		else puts("Poor Xed");
	}
	return 0;
}

posted @ 2020-04-05 11:25  bifanwen  阅读(151)  评论(0编辑  收藏  举报