题目链接http://poj.org/problem?id=3687
题目大意:
拓扑排序
给出一系列的关系,也就是a b两个数,必须保证 贴a标签的球在贴b标签的球的前面,最后排出一个序列,但是要注意的是最后的输出不是这个序列,而是从标签1到标签n所对应的球的重量,如果有多个解,那么就要让前面的标签所对应的重量尽可能的小。。
一开始的思路是用一个优先队列,从而做到从小到大取标签,保证标签号小的在序列的尽可能前面,这样对于比较小的标签,就可以得到比较小的重量了。
交了第一次,妥妥的WA了,因为我输出的整个标签的序列而不是每个标签所对应的球的重量。改完以后交了,还是错,想了好久,看了别人的思路都说这道题要反向建图,在从大往小的取,将大的数尽量放到答案的后面,这样就能够保证小重量的球尽可能的放到了前面。
看到一姐的博客说:每一次让序号大的数尽量往后排,因为从后往前排的时候,决定的位置是越来越重要的。
如果是正向构图的话很容易找出一个反例,
5 4
5 2
2 1
4 3
3 1
如果正向构图那么就是5->2->1 4->3->1 这样的话标签序列就会是(4 3 5 2 1) 所对应的答案就是(5 4 2 1 3)
如果反向构图从大往小的取,并尽可能的将大的放到后面话(也就是将相互之间的方向颠倒一下) 5<-2<-1 4<-3<-1 标签序列就变成(5 2 4 3 1) 答案就是(5 2 4 3 1 )
反向的话才可以找到最优解,,如果正向的话每次取最小,前面的位置是比后面的位置来的重要的,所以并不能保证之前的就是足够小的了,可能有比他更小的情况存在。
反着来,将最大的放到越后面,这样剩下的就是比较小的,也就可以放到前面更加重要的位置中去了。
不过不知道怎么证明这种算法的正确性
这题感觉蛮好的,以后还要再来看看。
代码:
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <cstdio>
using namespace std;
#define M 50000
#define INF 1000000
int n,m;
int flag;
vector<int> G[M];
int indegree[M];
int judge[300][300];
int ans[M];
void toposort()
{
priority_queue<int> q; //优先队列 从大的往小的取
int num = n;
for(int i = 1;i <= n;i++)
{
if(indegree[i]==0)
q.push(i);
}
while(!q.empty())
{
int cur = q.top();
q.pop();
ans[cur] = num; // 将每个标签从后往前放
num--;
for(int i = 0;i <= n;i++)
{
if(judge[cur][i]==1)
{
indegree[i]--;
if(!indegree[i])
q.push(i);
}
}
}
if(num>0) flag = 1; //判环
}
int main()
{
int t;
cin >> t;
while(t--)
{
flag = 0;
cin >> n >> m;
memset(indegree,0,sizeof(indegree));
memset(judge,0,sizeof(judge));
for(int i = 0;i < m;i++)
{
int a,b;
cin >> a >> b;
if(a==b) flag =1;
if(judge[b][a]==1) continue; // 判重即是否之前有一样的
judge[b][a] = 1; //反向构图就是将两个元素的指向相反
if(flag) continue;
indegree[a]++;
}
toposort();
//getchar();
if(flag) cout << -1 << endl;
else
{
for(int i = 1;i < n;i++)
cout << ans[i] << " ";
cout << ans[n] << endl;
}
}
return 0;
}