热身赛5

B - Brexit Negotiations

https://vjudge.net/problem/Gym-102483B

描述:

As we all know, Brexit negotiations are on their way—but we still do not know whether they will actually finish in time.

The negotiations will take place topic-by-topic. To organise the negotiations in the most effective way, the topics will all be discussed and finalised in separate meetings, one meeting at a time.

This system exists partly because there are (non-cyclic) dependencies between some topics: for example, one cannot have a meaningful talk about tariffs before deciding upon the customs union. The EU can decide on any order in which to negotiate the topics, as long as the mentioned dependencies are respected and all topics are covered.

Each of the topics will be discussed at length using every available piece of data, including key results from past meetings. At the start of each meeting, the delegates will take one extra minute for each of the meetings that has already happened by that point, even unrelated ones, to recap the discussions and understand how their conclusions were reached. See the figure for an example.

Nobody likes long meetings. The EU would like you to help order the meetings in a way such that the longest meeting takes as little time as possible.

img

Illustration of how time is spent in each meeting in a solution to Sample Input 2.

Input

The input consists of:

  • One line containing an integer nn (), the number of topics to be discussed. The topics are numbered from 11 to nn.

  • nn

     

lines, describing the negotiation topics.

The iith such line starts with two integers eiei and didi (1≤ei≤1061≤ei≤106, 0≤di<n0≤di<n), the number of minutes needed to reach a conclusion on topic ii and the number of other specific topics that must be dealt with before topic ii can be discussed.

The remainder of the line has di distinct integers , the list of topics that need to be completed before topic ii.

It is guaranteed that there are no cycles in the topic dependencies, and that the sum of didi over all topics is at most 4⋅1054⋅105.

Output

Output the minimum possible length of the longest of all meetings, if meetings are arranged optimally according to the above rules.

题目大意:给出n个会议,包括会议时间和该会议需要的前提会议(必须开完前提会议才可以开这个会议)。求一个会议顺序使得最长的会议时间最短(这里的会议时间是指会议时间加会议序号减1)。

读完题,感觉应该是堆加拓扑排序。然后立刻写了一发:

#include<iostream>
#include<queue>
using namespace std;
struct pt{
int r,t;
}a[4100000];
struct EDGE{
int to,nxt;
}e[4100000];
struct VD
{int v,dis;
bool operator<(const VD &a)const
{return a.dis>dis;
}
};
int n,b,c,num=0,head[4100000],ans[4100000],tt=0,anst=0;
priority_queue<VD>q;
int main(){

cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].t;
cin>>b;a[i].r=b;
for(int j=1;j<=b;j++){
cin>>c;

e[++num].to=i;
e[num].nxt=head[c];
head[c]=num;
}
}

for(int i=1;i<=n;i++){
if(a[i].r==0){
VD temp;
temp.v=i;
temp.dis =a[i].t;
q.push(temp);
}  
}
while(!q.empty()){
VD temp=q.top() ;
q.pop() ;
ans[++tt]=temp.v;
for(int i=head[temp.v];i;i=e[i].nxt){
a[e[i].to].r--;
if(a[e[i].to].r==0){
VD temp;
temp.v=e[i].to;
temp.dis =a[e[i].to].t;
q.push(temp);
}
}
}
/*for(int i=1;i<=tt;i++){
cout<<ans[i]<<' ';
}cout<<endl;*/
for(int i=1;i<=tt;i++){
anst=max(anst,a[ans[i]].t+i-1);
}
cout<<anst;
return 0;
}

每次把入度为0的点加到优先队列里面,然后把里面时间最长的取出来,把他的每个后续的入度都减一。然后重复操作,得出来的取出顺序就是题目要求的顺序,然后再遍历一遍求最大值。

当然上述只是我认为的,实际上错了。通过和队友讨论还有看题解知道应该是这样,建立反图然后堆加拓扑排序。也就是反过来把出度当作入度,然后将时间小的尽可能往后放。

#include<iostream>
#include<queue>
using namespace std;
struct pt{
int r,t;
}a[4100000];
struct EDGE{
int to,nxt;
}e[4100000];
struct VD
{int v,dis;
bool operator<(const VD &a)const
{return a.dis<dis;
}
};
int n,b,c,num=0,head[4100000],ans[4100000],tt=0,anst=0;
priority_queue<VD>q;
int main(){

cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].t;
cin>>b;
for(int j=1;j<=b;j++){
cin>>c;
a[c].r++;
e[++num].to=c;
e[num].nxt=head[i];
head[i]=num;
}
}

for(int i=1;i<=n;i++){
if(a[i].r==0){
VD temp;
temp.v=i;
temp.dis =a[i].t;
q.push(temp);
}  
}
while(!q.empty()){
VD temp=q.top() ;
q.pop() ;
ans[++tt]=temp.v;
for(int i=head[temp.v];i;i=e[i].nxt){
a[e[i].to].r--;
if(a[e[i].to].r==0){
VD temp;
temp.v=e[i].to;
temp.dis =a[e[i].to].t;
q.push(temp);
}
}
}
/*for(int i=1;i<=tt;i++){
cout<<ans[i]<<' ';
}cout<<endl;*/
for(int i=1;i<=tt;i++){
anst=max(anst,a[ans[i]].t+tt-i);
}
cout<<anst;
return 0;
}

两个代码差距特别小,就是反向建图,然后把最大优先队列变成了最小优先队列。但是我们现在来思考一下为什么。

如果正着,主要是上面例子那样,碰到两个点时间相等等于a,然后需要比较两个分支后面一个数谁更大谁放在前面,通过这个判断选哪个分支。

倒着的话,和刚才完全相反,碰到两个点时间相等等于a,然后需要比较两个分支后面一个数谁更小谁放在后面,但是比a小且在a前面,他的时间一定比a这个点的时间小。所以谁在后面无所谓。因为都比这个等于a的点小。

主要矛盾就是正向碰到相等的需要判断谁在前面谁在后面,但是倒着就不需要判断。因为要找的是最大时间最小的。如果还不明白可以再想一想,在纸上画一画。(我想了挺久的,感觉还是没有说清楚,可能有更好的理解思路)

原文地址:https://www.cnblogs.com/xcsj/p/13254085.html