考点
拓扑排序,贪心(思想)
思路
比较神奇的一道题。
一开始按照以往的思路。对于每对轻重关系,从轻的编号向重的编号连边。
在将重量与编号对应时,从小到大枚举每个重量。对于每个重量,从1到n枚举每个编号,将当前的重量赋给入度为零的编号中最小的一个。
以下的数据就可以hack掉
1
4 \ 1
3\ 2
正确的做法是,对于每对轻重关系,从重的编号向轻的编号连边。
在将重量与编号对应时,从大到小枚举每个重量。对于每个重量,从n到1枚举每个编号,将当前的重量赋给入度为零的编号中最小的一个。
上面的思路错在哪里呢?
每次只将当前的重量赋给当前入度为零的最小编号。看似保证了较小编号对应的重量也较小。但是有可能当前的较小编号后面连着的编号较大,而当前较大的编号后面可能连着的编号较小,甚至可能连着的是编号1。就像上面的hack数据一样。按照这种思路,是没有办法快速找到较小的编号以及1编号的。也就是说,每次贪心地挑走最小的重量赋给当前最小的编号,不断留下更大的重量,如果后面还有更小的编号可以对应当前的重量,但是该重量已经被挑走了,只能对应更大的重量,那么就错了。
下面的思路为什么对呢?
每次贪心地将当前的重量赋给当前入度为零的最大编号。每次贪心地挑走最大的重量赋给当前最大的编号,不断留下更小的重量,即使后面有更小的编号也可以对应更小的重量就不会出现上述情况。
code
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=211;
bool map[maxn][maxn];
int deg[maxn];
int ans[maxn];
int t;
int n,m;
bool flg;
void init()
{
memset(map,false,sizeof(map));
memset(deg,0,sizeof(deg));
memset(ans,0,sizeof(ans));
flg=true;
}
void toposort(int k)
{
while(k>=n){
bool sev=false;
for(int i=n;i>=1;i++){
if(deg[i]==0){
sev=true;
ans[i]=k;
deg[i]--;
k--;
for(int j=1;j<=n;j++){
if(map[i][j]){
map[i][j]=false;
deg[j]--;
}
}
break;
}
}
if(!sev){
flg=false;
break;
}
}
}
int main()
{
scanf("%d",&t);
while(t--){
init();
scanf("%d%d",&n,&m);
if(m==0){
for(int i=1;i<=n;i++)
printf("%d ",i);
puts(" ");
continue;
}
else{
for(int i=1;i<=m;i++){
int a,b;scanf("%d%d",&a,&b);
if(a==b) flg=false;
if(!map[b][a]){
map[b][a]=true;
deg[a]++;
}
}
//for(int i=1;i<=n;i++)
// printf("%d ",deg[i]);
if(flg) toposort(n);
if(flg){
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
puts(" ");
}
else{
puts("-1");
continue;
}
}
}
return 0;
}