【[BZOJ1612][Usaco2008 Jan]Cow Contest奶牛的比赛 】从拓扑排序到DFS再到拓扑排序

思路:把这题抽象为一个图以后,当且只有这种情况下,此节点的排名是确定的:
AC思路:

  • 比这个节点排名高的数目加上比这个节点排名低的数目等于n-1;
  • 所以这里可以双向建图,用两个dfs求出节点的上下祖先数目。

我一开始的思路是用拓扑排序的,用dfs、backtracking得到所有的排序序列,如果当一个结点在所有的拓扑序列中排名都没有发生变化时ans++,然后经历了从MLE,优化,TLE,优化,若干CE,到放弃的过程!!WAWAWA代码如下:

//#include <bits/stdc++.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
using namespace std;
vector<vector<int> > graph(105,vector<int>(105,0));
vector<int> in(105,0);//入度
vector<int> path(105,0); 
map<int,int> mp; 
int vis[1005];
//爆栈了咕咕咕咕 
int n,m,f=0,row=0;
//topo
void dfs(int depth){
	if(depth==n){
		row++;
		//扫一遍
	    if(!f){
	    	f=1;
	    	for(int i=0;i<n;i++)	mp[i] = path[i];
		}else{
			if(mp.empty())	return ;
			set<int> st;
			map<int,int> ::iterator it;
            for(it=mp.begin();it!=mp.end();++it){
            	if(it->second!=path[it->first]){
            		st.insert(it->first);
				}
			}
			set<int> ::iterator sit;
			for(sit = st.begin();sit!=st.end();++sit){
				mp.erase(*sit);
			}
		}
	}else{
		for(int i=1;i<=n;i++){
			if(!in[i]&&!vis[i]){//入度为0
				 for(int j=1;j<=n;j++){//刷新入度 
				 	if(graph[j][i]){
				 		in[j]--;
					 }
				 }
				 vis[i] = 1;
				 path[depth] = i;
				 dfs(depth+1);
				 for(int j=1;j<=n;j++){
				 	if(graph[j][i]){
				 		in[j]++;
					 }
				 }
				 vis[i] = 0;
			}
		}
	}
}
int main(){
	cin>>n>>m;
	while(m--){
		int a,b;
		cin>>a>>b;
		graph[a][b] = 1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(graph[i][j]){
				in[i]++;
			}
		}
	}
	dfs(0);
	cout<<mp.size();
	return 0;
}

AC代码“两个DFS”:

//#include <bits/stdc++.h>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
vector<vector<int> > graph1(105,vector<int>(105,0));
vector<vector<int> > graph2(105,vector<int>(105,0));
int vis[1005];
int n,m,cnt1,cnt2,ans=0;
void dfs1(int x){
	vis[x] = 1;
	cnt1++;
	for(int i=1;i<=n;i++){
		if(graph1[x][i]){
			if(!vis[i])
			dfs1(i);
		}
	}
}
void dfs2(int x){
	vis[x] = 1;
	cnt2++;
	for(int i=1;i<=n;i++){
		if(graph2[x][i]){
			if(!vis[i])
			dfs2(i);
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		int a,b;
		scanf("%d%d",&a,&b);
		graph1[a][b] = 1;
		graph2[b][a] = 1;
	}
	for(int i=1;i<=n;i++){
		cnt1=0,cnt2=0;
		memset(vis,0,sizeof(vis));
		dfs1(i);
		memset(vis,0,sizeof(vis));
		dfs2(i);
		//cout<<cnt1<<" "<<cnt2<<endl;
		if(cnt1+cnt2==n+1)
		ans++;
	}
	printf("%d",ans);
	return 0;
}

写到这里我还是希望能用到拓扑排序解决这个问题,基于刚才的“两个DFS”解法:
思路:

  • 寻找图中入度为0的点,然后把它从图中删除,刷新各个节点入度;
  • 当图中入度为0的结点个数为1时,我们需要判断它的祖先数目和已经删除的结点数目是否相等,这里求祖先数目就使用到了一个DFS,代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
vector<vector<int> > graph(105,vector<int>(105,0));
vector<int> in(105,0);//入度
vector<int> pre(105,0); 
int vis[105];
int n,m,cnt,ans=0,del=0;
void dfs(int x){
	vis[x] = 1;
	cnt++;
	for(int i=1;i<=n;i++){
		if(graph[i][x]){
			if(!vis[i])
			dfs(i);
		}
	}
}
//topp
void topo(){
	int del=0;
	while(del<n){
		vector<int> s;
		for(int i=1;i<=n;i++){
			if(in[i]==0&&!vis[i]){
				s.push_back(i);
				vis[i]=1;
				del++;
			}
	   }
		for(int i=0;i<s.size();i++){
				for(int j=1;j<=n;j++){
				    if(graph[s[i]][j]){
				    	in[j]--;
					}
		       }
		}
	   if(s.size()==1&&del==pre[s[0]])    ans++;  
	}	
}
int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		int a,b;
		scanf("%d%d",&a,&b);
		graph[a][b] = 1;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(graph[i][j]){
				in[j]++;
			}
		}
	}
	for(int i=1;i<=n;i++){
		cnt=0;
		memset(vis,0,sizeof(vis));
		dfs(i);
		pre[i] = cnt;
	}
	memset(vis,0,sizeof(vis));
	topo();
	printf("%d",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Csdn_jey/article/details/89042339