首页 > 代码库 > [HNOI2015]菜肴制作 题解(自带口胡证明)

[HNOI2015]菜肴制作 题解(自带口胡证明)

[HNOI2015]菜肴制作

时间限制: 1 Sec  内存限制: 512 MB

题目描述

知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴。 

ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予
1到N的顺序编号,预估质量最高的菜肴编号为1。由于菜肴之间口味搭配的问题,
某些菜肴必须在另一些菜肴之前制作,具体的,一共有 M 条形如“i 号菜肴‘必须’
先于 j 号菜肴制作”的限制,我们将这样的限制简写为<i,j>。现在,酒店希望能求
出一个最优的菜肴的制作顺序,使得小 A能尽量先吃到质量高的菜肴:也就是说,
(1)在满足所有限制的前提下,1 号菜肴“尽量”优先制作;(2)在满足所有限制,1
号菜肴“尽量”优先制作的前提下,2号菜肴“尽量”优先制作;(3)在满足所有限
制,1号和2号菜肴“尽量”优先的前提下,3号菜肴“尽量”优先制作;(4)在满
足所有限制,1 号和 2 号和 3 号菜肴“尽量”优先的前提下,4 号菜肴“尽量”优
先制作;(5)以此类推。 
例1:共4 道菜肴,两条限制<3,1>、<4,1>,那么制作顺序是 3,4,1,2。例2:共
5道菜肴,两条限制<5,2>、 <4,3>,那么制作顺序是 1,5,2,4,3。例1里,首先考虑 1,
因为有限制<3,1>和<4,1>,所以只有制作完 3 和 4 后才能制作 1,而根据(3),3 号
又应“尽量”比 4 号优先,所以当前可确定前三道菜的制作顺序是 3,4,1;接下来
考虑2,确定最终的制作顺序是 3,4,1,2。例 2里,首先制作 1是不违背限制的;接
下来考虑 2 时有<5,2>的限制,所以接下来先制作 5 再制作 2;接下来考虑 3 时有
<4,3>的限制,所以接下来先制作 4再制作 3,从而最终的顺序是 1,5,2,4,3。 
现在你需要求出这个最优的菜肴制作顺序。无解输出“Impossible!” (不含引号,
首字母大写,其余字母小写) 

输入

 第一行是一个正整数D,表示数据组数。 

接下来是D组数据。 
对于每组数据: 
第一行两个用空格分开的正整数N和M,分别表示菜肴数目和制作顺序限
制的条目数。 
接下来M行,每行两个正整数x,y,表示“x号菜肴必须先于y号菜肴制作”
的限制。(注意:M条限制中可能存在完全相同的限制) 

输出

 输出文件仅包含 D 行,每行 N 个整数,表示最优的菜肴制作顺序,或

者”Impossible!”表示无解(不含引号)。 

样例输入

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先于菜肴2制作,菜肴2先于菜肴3制作,菜肴3先于
菜肴1制作,而这是无论如何也不可能满足的,从而导致无解。 
100%的数据满足N,M<=100000,D<=3。 
  这道题当时斯巴达了大半个下午,然后实在受不了了好无耻好无耻的看了题解,然后智商收到了一万点精神暴击。
  这道题几乎所有人都会入的坑就是贪心去找当前最小,如果你还没调出来,你可以试一试以下数据:

1
6 6
6 5
6 4
4 3
3 1
2 1
5 2

 正解是6 5 2 4 3 1,如果我没猜错你的答案是6 4 3 5 2 1,但是2是能靠前就靠前的,因此这种做法是错误的。  常规做法有一个很大的弊端,就是他忽略了他向下找入度为0的点的途中所遇到的点,这就很尴尬了,那我们应该怎么搞呢?

  正解是倒着搞一遍,把边和入度全部打过来,贪心找最大的,最后答案也要倒着输出,不难发现这样一下子就A了,然而具体原因我和Q某犇在一起捣鼓了半天才貌似给出了证明,如有不对请指正。

  首先,我们之所以不去正着贪心是因为这样会忽略一个比当前点更小的点得存在,也就不得不将它本来应当存在的位置靠后,而倒着找就会忽略比当前点都大的点的位置,然而如果我们直接去找最大的位置的话那么必须经过的那个更小的点的实际访问顺序就会越靠后,然而实际并不是这样,越小的是越尽量靠前的,因此如果这样做反而是错的了,但对于正着搜就不然,然而我们貌似不好或者无法实现……

  

技术分享
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<map>
 7 #include<queue>
 8 #include<string>
 9 #include<cmath>
10 using namespace std;
11 int t,n,m,zz,zz2,rd[100005];
12 int a[100005],b[100005],jg[100005];
13 struct ro{
14     int to;
15     int next;
16 }road[100005],road2[100005];
17 void build(int x,int y){
18     zz++;
19     road[zz].to=y;
20     road[zz].next=a[x];
21     a[x]=zz;
22 }
23 priority_queue<int> q1;
24 int main(){
25     scanf("%d",&t);
26 while(t--)
27 {
28     memset(rd,0,sizeof(rd));
29     memset(a,0,sizeof(a));
30     zz=0;
31     zz2=0;
32     memset(b,0,sizeof(b));
33     scanf("%d%d",&n,&m);
34     for(int i=1;i<=m;i++)
35     {   
36         int x,y;
37         scanf("%d%d",&x,&y);
38         build(y,x);
39         rd[x]++;
40     }
41     for(int i=1;i<=n;i++)
42     {
43         if(!rd[i])
44             q1.push(i);
45     }
46     while(!q1.empty())
47     {
48         int x=q1.top();
49         q1.pop();
50         zz2++;
51         jg[zz2]=x;
52         for(int i=a[x];i>0;i=road[i].next)
53         {
54             int y=road[i].to;
55             rd[y]--;
56             if(!rd[y])
57                 q1.push(y);
58         }
59     }
60     if(zz2==n)
61     {
62         for(int i=zz2;i>0;i--)
63             printf("%d ",jg[i]);
64         printf("\n");
65     }
66     else
67     {
68         printf("Impossible!\n");
69     }
70 }
71     //while(1);
72     return 0;
73 }
View Code

 附上同时和我一起做这道题的Q某犇的题解(貌似他的证明和我不太一样)传送门->QTY_

[HNOI2015]菜肴制作 题解(自带口胡证明)