ZOJ 4124 /2019山东省赛 (图论)

省赛的时候我觉得我的思路应该没问题,但是只过了第一组数据,然后我调试了半个小时…
今天有空重写了一遍,然后过了…
当时应该是错把小于号写成了大于等于号,但也没啥可抱怨的,因为即使做出来了这个题我们也是和现在一样的银牌 : )

根据所有点的大小状态可以建立一个有向图。
分析可知:一个数可以是中值数,当且仅当小于该数字的总个数小于"小于中值数的个数",并且大于该数字的总个数也小于"大于中值数的个数"。
转化为图论语言即:目标点为中值点,当且仅当该节点的前继节点总数小于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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值