思路:把这题抽象为一个图以后,当且只有这种情况下,此节点的排名是确定的:
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;
}