刷题记录:牛客NC20115[HNOI2015]菜肴制作[对反向拓扑正确性进行了详细有通俗易懂的证明]

传送门:牛客

题目描述:

题目较长.此处省略
输入:
3 
5 4 
5 4 
5 3 
4 2 
3 2 
3 3 
1 2 
2 3 
3 1 
5 2 
5 2 
4 3
输出:
1 5 3 4 2 
Impossible! 
1 5 2 4 3

感觉是一道挺难的拓扑排序题.思维难度挺高

主要思路:

  1. 首先这道题给定了两个数之间的位次关系,那么显然是需要用到拓扑排序的.输出不可行的方法也十分简单,只要使用拓扑排序判断一下有没有形成环即可,因为假设形成了环了话,那么我们的这个环就不存在一个点的入度为0,那么这个点就不会加入到我们的BFS中,这样的话我们最终的答案队列中的数就不够 n n n个了,所以此时直接输出 i m p o s s i b l e ! impossible! impossible!即可
  2. 那么接下来的主要解决问题就是如何保证越小的值越在前面这个问题了.首先我们肯定会想到使用贪心输出字典序最小的序列,但是遗憾的是,这个贪心显然是错误的.举个反例
    4 种菜肴,限制为 < 2 , 4 > < 3 , 1 > 那么字典序最小的是 2 , 3 , 1 , 4 但题目要求的最优解是 3 , 1 , 2 , 4 4种菜肴,限制为<2,4><3,1>那么字典序最小的是2,3,1,4但题目要求的最优解是3,1,2,4 4种菜肴,限制为<2,4><3,1>那么字典序最小的是2,3,1,4但题目要求的最优解是3,1,2,4
    所以我们直接使用贪心做时不行的.
  3. 但是我们有这样的一个想法,就是将越大的数字越往后面放,就用拓扑排序建反图,然后遍历出一个字典序最大的序列,这样做是可以满足我们的题意的,为什么呢,接下来我简单的证明一下:

当我们有这样的一段序列 1 , 2 , . . . . n 1,2,....n 1,2,....n,然后此时因为限制有了一段拓扑序,此时对于最后一个位置的数字有两种可能性:

  1. 首先是我们的最后的一位数因为限制只有一种情况,也就是最后一个位置只能填那个数字,那么这种情况没什么好讨论的
  2. 然后是我们的最后一位数字有多种情况,此时我们讨论有两种情况的时候(因为当有多种情况时,我们都可以转化成两两比较的形式形成两种情况),对于两种情况我们分别记为 x , y x,y x,y ,并且 x > y x>y x>y,我们先想一下我们的 x , y x,y x,y是可以放在最后一个位置的,那么这里有几个隐含的拓扑条件,首先我们的 x , y x,y x,y之间肯定是没有拓扑关系的,然后呢所有其他数对于我们的 x , y x,y x,y要么没有拓扑关系,要么就是有 x , y x,y x,y是相对于之前的数字是在后面的拓扑关系.那么此时的话假设我们的 y y y放在最后一位更优,那么此时我们的 x x x两种位置的可能性,一种是 x x x就在我们的 y y y的前面一个位置, a 1 , a 2 , a 3... x , y a1,a2,a3...x,y a1,a2,a3...x,y此时的话我们显然可以将我们的 x x x y y y进行一个换位,此时不改变拓扑序并且我们让较小的值放在了前面,显然是更优的.一种是 x x x在一段序列的中间,例如下列情况 a 1 , a 2 , a 3... x , a k , a k + 1 . . . a k + n , y a1,a2,a3...x,a_k,a_{k+1}...a_{k+n},y a1,a2,a3...x,ak,ak+1...ak+n,y,这种情况下的话,对于我们的 a k ak ak以及一直到 a k + n a_{k+n} ak+n来说的话,我们这些数字和我们的 x x x肯定是不存在拓扑序关系的,不然的话和我们之前的隐含的拓扑关系矛盾了,也就是说此时我们的 x x x如果放在我们的序列的末尾的话,我们的拓扑序是没有发生变化的,所以此时我们的 x x x也可以安心的移动到最后一位,那么此时的话显然我们又在不改变拓扑序的情况下将我们较小的值放在其那面了,显然是更优的,那么总结一下,肯定是最大的可以放的值放在最后一个位置更优
  3. 那么我们根据上述情况递归一下,显然可以推出所有的情况.

至此,证毕


下面是具体的代码部分:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int T;int n,m;int in[maxn];int ans[maxn];int cnt=0;
vector<int>edge[maxn];
void init() {
	memset(in,0,sizeof(in));
	memset(ans,0,sizeof(ans));
	memset(edge,0,sizeof(edge));
	cnt=0;
}
void BFS(int S) {
	priority_queue<int>q;
	q.push(S);
	while(!q.empty()) {
		int u=q.top();q.pop();
		if(u!=0) ans[++cnt]=u;
		for(int i=0;i<edge[u].size();i++) {
			int v=edge[u][i];
			in[v]--;
			if(in[v]==0) {
				q.push(v);
			}
		}
	}
}
int main() {
	T=read();
	while(T--) {
		init();
		n=read();m=read();
		for(int i=1;i<=m;i++) {
			int u=read(),v=read();
			edge[v].push_back(u);
			in[u]++;
		}
		for(int i=1;i<=n;i++) {
			if(in[i]==0){
				edge[0].push_back(i);
				in[i]++;
			} 
		}
		BFS(0);
		if(cnt!=n) printf("Impossible!\n");
		else {
			for(int i=cnt;i>=1;i--) printf("%d ",ans[i]);
			printf("\n");
		}
	}
	return 0;
}

你可能感兴趣的:(c++算法,算法,c++,图论,拓扑排序)