BZOJ4010 [HNOI2015]菜肴制作[拓扑排序+贪心]
其实有些题真的是,即使翻看了题解,也可以解释一二原因,也并不能对这种做法有深深的认同感。。
因为题意说的是让序号小的优先在拓扑序中向左靠,也就是说,比如1号会被前面很多大序号的节点“堵住”,但是仍要优先让他先出来,所以这时候按字典序最小来做拓扑是不对的。。`````
反过来考虑:
引理:对于一张DAG,其任意一个拓扑序的倒序和这张反向的DAG的拓扑序一一对应。
证明:不会证明。对于出现在原拓扑序末尾的一段,一定是没有出度的节点,那么反图中,他们没有入度,所以可以出现在拓扑序开头,而末尾再向前一段的点(假设为$x$)是有出度的,且指向这些没有出度的点,这在反图中就对应了没有入度的点删掉后,继续选择$x$为零入度点。以此类推。所以正拓扑序反序和反图拓扑序对应,同理,后者也与前者对应。
反正这个是超有道理的啦(っ ̄▽ ̄)っ。
于是,建一张反的DAG,并且采用字典序从大到小的方法拓扑排序。为什么这样做?因为,首先反向的拓扑序是和正向拓扑序等价的,这是前提。然后,在所有入度为0的点里,我们要选择最大的,因为,如果我们先选择了小序号的放在后面,那么明显他应该再往左靠,也就是说,小数字比大数字向左靠的优先级高。这样,一个拓扑序中,大数尽量放后面,“让”给小数,让小数尽量靠左。这样的贪心就是对的。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #define mst(x) memset(x,0,sizeof x) 8 #define dbg(x) cerr << #x << " = " << x <<endl 9 #define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl 10 using namespace std; 11 typedef long long ll; 12 typedef double db; 13 typedef pair<int,int> pii; 14 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 15 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 16 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 17 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 18 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 19 template<typename T>inline T read(T&x){ 20 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 21 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 22 } 23 const int N=1e5+7; 24 int T,n,m,flag; 25 struct thxorz{int nxt,to;}G[N]; 26 int Head[N],tot; 27 inline void Addedge(int x,int y){G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot;} 28 #define y G[j].to 29 //int dfn[N],low[N],cnt,stk[N],instk[N],Top; 30 //void tarjan(int x){ 31 // dfn[x]=low[x]=++cnt,stk[++Top]=x,instk[x]=1; 32 // for(register int j=Head[x];j;j=G[j].nxt){ 33 // if(!dfn[y])tarjan(y),MIN(low[x],low[y]); 34 // else if(instk[y])MIN(low[x],dfn[y]); 35 // } 36 // if(dfn[x]==low[x]){ 37 // int tmp,cnt=0; 38 // do instk[tmp=stk[Top--]]=0,++cnt;while(tmp^x); 39 // if(cnt>1)flag=1; 40 // } 41 //} 42 int ans[N],r,deg[N]; 43 priority_queue<int> pq; 44 inline void toposort(){ 45 for(register int i=1;i<=n;++i)if(!deg[i])pq.push(i); 46 while(!pq.empty()){ 47 ans[++r]=pq.top();pq.pop(); 48 for(register int j=Head[ans[r]];j;j=G[j].nxt) 49 if(!(--deg[y]))pq.push(y); 50 } 51 } 52 #undef y 53 54 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout); 55 read(T);while(T--){ 56 read(n),read(m); 57 flag=tot=r=0;mst(Head),mst(deg);//mst(dfn),mst(low); 58 for(register int i=1,x,y;i<=m;++i)read(x),read(y),Addedge(y,x),++deg[x]; 59 // for(register int i=1;i<=n;++i)if(!dfn[i])Top=0,tarjan(i); 60 // if(flag){puts("Impossible!");continue;} 61 toposort(); 62 if(r<n){puts("Impossible!");continue;} 63 for(register int i=n;i;--i)printf("%d ",ans[i]);puts(""); 64 } 65 return 0; 66 }
总结:正向不便不妨建反图考虑。然而还是个套路、