poj2687

考点

拓扑排序,贪心(思想)


思路

比较神奇的一道题。

一开始按照以往的思路。对于每对轻重关系,从轻的编号向重的编号连边。

在将重量与编号对应时,从小到大枚举每个重量。对于每个重量,从1到n枚举每个编号,将当前的重量赋给入度为零的编号中最小的一个。

这次是真WA了

以下的数据就可以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;
}
上一篇:代数学笔记: 域扩张(二)


下一篇:软件生命周期模型知识点总结(瀑布模型、演化模型、增量模型、V模型、W模型、螺旋模型、构件组装模型、RAD模型、RUP模型、极限编程模型)