JXYC集训-图论1 题解

A - 图的存储与出边的排序

vector存图,排序后输出

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m;
vector<int>g[N];
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) g[i].clear();
	for(int i=1;i<=m;i++){
		int u,v;cin>>u>>v;
		g[u].push_back(v);
	}
	for(int i=1;i<=n;i++) sort(g[i].begin(),g[i].end());
	for(int i=1;i<=n;i++){
		for(int v:g[i]) cout<<v<<" ";
		cout<<'\n';
	}
}
int main()
{
	//freopen("a.txt","r",stdin);
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;cin>>t;
	while(t--){solve();}
    return 0;
}

B - 拓扑排序 / 家谱树

拓扑排序模板题,可以边做边输出

#include <bits/stdc++.h>
using namespace std;
const int N=200;
int n,m,deg[N];
vector<int>g[N];
vector<int>ans;
void ts(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(deg[i]==0) {q.push(i);cout<<i<<" ";}
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		//cout<<u<<'\n';
		for(int v:g[u]){
			deg[v]--;
			if(deg[v]==0){
				cout<<v<<" ";
				q.push(v);
			}
		}
	}
}
int main(){
	cin>>n;
	int v;
	for(int i=1;i<=n;i++){
		while(true){
			cin>>v;
			if(v==0) break;
			g[i].push_back(v);deg[v]++;
		}
	}
	ts();
	//for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
	return 0;
}

C - A Bug's Life

二分图判定模板题,DFS染色/并查集均可。

#include <iostream>
using namespace std;
const int N=500002;
int father[N],n,m,x,y,z;
int Find(int x) {
	return father[x] == x ? x : (father[x] = Find(father[x]));
}

bool Union(int i, int j) {
	int x = Find(i);
	int y = Find(j);

	if (x != y) {
		father[x] = y;
		return true;
	}
	return false;
}
void solve(int cas){
	cin>>n>>m;
	for(int i=1;i<=2*n;i++) father[i]=i;
	//bool f=true;
	for(int i=1;i<=m;i++){
		int u,v;cin>>u>>v;
		Union(u+n,v);Union(u,v+n);
	}
	cout<<"Scenario #"<<cas<<":\n";
	for(int i=1;i<=n;i++){
		if(Find(i)==Find(i+n)){
			cout<<"Suspicious bugs found!\n\n";
			return;
		}
	}
	cout<<"No suspicious bugs found!\n\n";
}
int main()
{
	//freopen("a.txt","r",stdin);
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;cin>>t;
	for(int i=1;i<=t;i++){
		solve(i);
	}
    return 0;
}

D - 封锁阳光大学

求二分图较小的一个集合。在并查集做完之后,每一个节点都会指向一个祖先,所以一个祖先就代表了一个块。分别记录块中较小的一部分,累加即可。

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m;
int father[N];
bool vis[N];
int a[N],b[N];
void Init(int n)
{
	for(int i=1;i<=n;i++) father[i]=i;
}
int Find(int x)
{
	return father[x]==x?x:(father[x]=Find(father[x]));
}
void Union(int a,int b)
{
	int x=Find(a),y=Find(b);
	if(x==y) return;
	father[x]=y;
}
int main(){
	//freopen("a.txt","r",stdin);
	cin>>n>>m;
	Init(n*2);
	for(int i=1;i<=m;i++){
		int u,v;cin>>u>>v;a[i]=u,b[i]=v;
		int x=Find(u),y=Find(v);
		if(x==y){
			cout<<"Impossible\n";return 0;
		}
		Union(u+n,v);Union(u,v+n);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		int x=Find(a[i]),y=Find(b[i]);
		if(vis[x]||vis[y]){
			continue;
		}
		vis[x]=vis[y]=true;
		int cnt1=0,cnt2=0;
		for(int j=1;j<=n;j++){
			if(Find(j)==x) cnt1++;
			if(Find(j)==y) cnt2++;
		} 
		ans+=min(cnt1,cnt2);
	}
	cout<<ans<<'\n';
	return 0;
}

E - Ping-Pong (Easy Version)

暴力DFS

#include <bits/stdc++.h>
using namespace std;
const int N=1000; 
int cnt;
bool vis[N];
struct node{
	int l,r;
}a[N];
void dfs(int u){
	vis[u]=true;
	for(int i=1;i<=cnt;i++){
		if(!vis[i]){
			if((a[i].l<a[u].l && a[u].l<a[i].r)||(a[i].l<a[u].r&&a[u].r<a[i].r)) dfs(i);
		}
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	//freopen("a.txt","r",stdin);
	int n;cin>>n;
	while(n--){
		int opt,l,r;
		cin>>opt>>l>>r;
		if(opt==1){
			a[++cnt]={l,r};
		}
		else{
			memset(vis,0,sizeof(vis));
			dfs(l);
			if(vis[r]) cout<<"YES\n";
			else cout<<"NO\n";
		}
	}
//aaefawef
	return 0;
}

F - 最大食物链计数

问题相当于求DAG上有多少条起点入度为0、终点出度为0的路径。拓扑排序中DP即可。

#include <bits/stdc++.h>
using namespace std;
const int N=6000;
const int mod=80112002;
int n,m,ans,deg[N],to[N];
int f[N];
vector<int>g[N];
void ts(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(deg[i]==0) {q.push(i);f[i]=1;}
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		//cout<<u<<'\n';
		for(int v:g[u]){
			deg[v]--;f[v]+=f[u];f[v]%=mod;
			if(deg[v]==0){
				if(to[v]==0) {ans+=f[v];ans%=mod;continue;}
				q.push(v);
			}
		}
	}
}
int main(){
	//freopen("a.txt","r",stdin);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;cin>>u>>v;
		g[u].push_back(v);deg[v]++;to[u]++;
	}
	ts();
	cout<<ans<<'\n';
	return 0;
}

G - Fox And Names

图论建模。把26个字母看作点,则可以根据给出的字符串,把字典序小的点连向字典序大的边,然后尝试拓扑排序。在输入的时候,对于绝对不合法的情况要特判。

#include <bits/stdc++.h>
using namespace std;
const int N=100000;
int n,tot,deg[N],ans[N];
vector<int>g[N];
string t,s;
int main(){
	//freopen("a.txt","r",stdin);
	cin>>n>>s;
	for(int i=1;i<n;i++){
		cin>>t; 
		int m=min(s.size(),t.size()),j;
		for(j=0;j<m;j++){
			if(s[j]!=t[j]){
				int x=s[j]-96,y=t[j]-96;
				g[x].push_back(y);
				deg[y]++;
				break;
			}
		}
		if(j>=m&&t.size()<s.size()){
			printf("Impossible");
			return 0;
		}
		s=t;
	}
	queue<int>q;
	for(int i=1;i<=26;i++) if(!deg[i]) q.push(i);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		ans[++tot]=x;
		for(auto a:g[x]){
			deg[a]--;
			if(!deg[a]) q.push(a);
		}
	}
	if(tot<26){
		cout<<"Impossible\n";
	}
	else{
		for(int i=1;i<=26;i++) cout<<(char)(ans[i]+'a'-1);
	}
	return 0;
}

H - Instrction Arrangement

关键路径模板,类似于差分约束,边权取负后SPFA跑最长路即可

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define INF 1e9
using namespace std;
const int maxn =1000+10;
const int maxm =20000+10;
 
struct Edge
{
    int from,to,dist;
    Edge(){}
    Edge(int f,int t,int d):from(f),to(t),dist(d){}
};
 
struct BellmanFord
{
    int n,m;
    int head[maxn],next[maxm];
    Edge edges[maxm];
    int d[maxn];
    bool inq[maxn];
 
    void init(int n)
    {
        this->n=n;
        m=0;
        memset(head,-1,sizeof(head));
    }
 
    void AddEdge(int from,int to,int dist)
    {
        edges[m]=Edge(from,to,dist);
        next[m]=head[from];
        head[from]=m++;
    }
 
    void bellmanford()
    {
        memset(inq,0,sizeof(inq));
        for(int i=0;i<n;i++) d[i]= i==0?0:INF;
        queue<int> Q;
        Q.push(0);
 
        while(!Q.empty())
        {
            int u=Q.front(); Q.pop();
            inq[u]=false;
            for(int i=head[u];i!=-1;i=next[i])
            {
                Edge &e=edges[i];
                if(d[e.to] > d[u]+e.dist)
                {
                    d[e.to] = d[u]+e.dist;
                    if(!inq[e.to])
                    {
                        inq[e.to]=true;
                        Q.push(e.to);
                    }
                }
            }
        }
    }
}BF;
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2)
    {
        BF.init(n+1);
        while(m--)
        {
            int u,v,d;
            scanf("%d%d%d",&u,&v,&d);
            ++u,++v;
            BF.AddEdge(v,u,-d);
        }
        for(int i=1;i<=n;i++)
            BF.AddEdge(0,i,0);
        BF.bellmanford();
        int max_v=-1,min_v=INF;
        for(int i=1;i<=n;i++)
        {
            max_v=max(max_v,BF.d[i]);
            min_v=min(min_v,BF.d[i]);
        }
        printf("%d\n",max_v-min_v+1);
    }
    return 0;
}

I - 寻找道路

建反图,处理出那些点可以走,那些不可以,之后用DFS/BFS/最短路均可

#include<bits/stdc++.h>
using namespace std;
int read()
{
    int x=0,y=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='0')y=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*y;
}
int n,m;
vector<int>v[10005];
bool vis[10005],er[10005];
queue<int>q;
int st,ed;
int ans[10005];
int main()
{    
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        int a=read(),b=read();
        if(a==b)continue;
        v[b].push_back(a);
    }
    st=read(),ed=read();
    vis[ed]=1;
    q.push(ed);
    while(!q.empty())
    {
        int no=q.front();
        q.pop();
        for(int i=0,j=v[no].size();i<j;i++)
            if(!vis[v[no][i]]){vis[v[no][i]]=1;q.push(v[no][i]);}
    }
    memcpy(er,vis,sizeof(vis));
    for(int i=1;i<=n;i++)
        if(!vis[i])
            for(int j=0,k=v[i].size();j<k;j++)
                if(er[v[i][j]])
                    er[v[i][j]]=0;
    q.push(ed);
    while(!q.empty())
    {
        int no=q.front();
        q.pop();
        for(int i=0,j=v[no].size();i<j;i++)
            if(er[v[no][i]])
            {
                q.push(v[no][i]);
                er[v[no][i]]=0;
                ans[v[no][i]]=ans[no]+1;
            }
    }
    if(ans[st]==0)cout<<-1<<'\n';
    else cout<<ans[st]<<'\n';
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值