省赛的时候我觉得我的思路应该没问题,但是只过了第一组数据,然后我调试了半个小时…
今天有空重写了一遍,然后过了…
当时应该是错把小于号写成了大于等于号,但也没啥可抱怨的,因为即使做出来了这个题我们也是和现在一样的银牌 : )
根据所有点的大小状态可以建立一个有向图。
分析可知:一个数可以是中值数,当且仅当小于该数字的总个数小于"小于中值数的个数",并且大于该数字的总个数也小于"大于中值数的个数"。
转化为图论语言即:目标点为中值点,当且仅当该节点的前继节点总数小于n/2,并且该节点的后继节点总数也小于n/2。
样例数据很走心的提醒了我们这个图可能成环。
通过以上分析可以写出算法:
1.判断成环
2.对于每个点,分别求前继和后继节点数,并判断是否在目标范围内
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=100+5;
int from[maxn][maxn],to[maxn][maxn]; //来去指向
int fcnt[maxn],tcnt[maxn]; //入度出度数量
int pre[maxn],aft[maxn]; //前继后继数目
int in[maxn]; //入度数目副本
int ans[maxn]; //答案
bool vis[maxn]; //访问标记
int m,n;
void predfs(int u){ //标计所有前继节点
if(vis[u]) return;
vis[u]=true;
for(int i=0;i<fcnt[u];i++) predfs(from[u][i]);
}
void aftdfs(int u){ //标记所有后继节点
if(vis[u]) return;
vis[u]=true;
for(int i=0;i<tcnt[u];i++) aftdfs(to[u][i]);
}
bool iscir(){ //拓扑排序判断环
for(int i=0;i<n;i++) in[i]=fcnt[i];
queue<int> q;
memset(vis,false,sizeof(vis));
for(int i=0;i<n;i++){
if(in[i]==0){
q.push(i);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
if(vis[u]) continue;
vis[u]=true;
for(int i=0;i<tcnt[u];i++){
int v=to[u][i];
if(vis[v]){
while(!q.empty()) q.pop();
return true;
}
if(in[v]>0) in[v]--;
if(in[v]==0) q.push(v);
}
}
for(int i=0;i<n;i++){
if(!vis[i]) return true;
}
return false;
}
int main(){
int t;
int u,v;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
memset(fcnt,0,sizeof(fcnt));
memset(tcnt,0,sizeof(tcnt));
memset(ans,0,sizeof(ans));
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
u--;
v--;
from[v][fcnt[v]]=u;
to[u][tcnt[u]]=v;
tcnt[u]++;
fcnt[v]++;
}
if(!iscir()){
memset(pre,0,sizeof(pre));
for(int i=0;i<n;i++){
memset(vis,false,sizeof(vis));
predfs(i);
for(int i=0;i<n;i++){
if(vis[i]) pre[i]++;
}
pre[i]--;
}
memset(aft,0,sizeof(aft));
for(int i=0;i<n;i++){
memset(vis,false,sizeof(vis));
aftdfs(i);
for(int i=0;i<n;i++){
if(vis[i]) aft[i]++;
}
aft[i]--;
}
int med=(n+1)/2;
for(int i=0;i<n;i++){
if(aft[i]<med&&pre[i]<med) ans[i]=1;
}
}
for(int i=0;i<n;i++) printf("%d",ans[i]);
putchar('\n');
}
return 0;
}