【题解】[USACO 2003 FALL] Popular Cows

题面

【题目描述】
每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。 这种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。你的任务是求出有多少头牛被所有的牛认为是受欢迎的。
【输入】
第一行两个数N,M。 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可能出现多个A,B)
【输出】
一个数,即有多少头牛被所有的牛认为是受欢迎的。
【样例输入】

3 3
1 2
2 1
2 3

【样例输出】

1

【数据范围】
100%的数据N<=10000,M<=50000

算法分析

强连通分量缩点。
在同一个强连通分量里,牛肯定是互相受欢迎的,因此需要考虑强连通分量之外的点。对所有强连通分量进行缩点,形成了一个有向无环图。
如存在以下关系:
在这里插入图片描述
进行缩点知之后,如下:在这里插入图片描述
通过观察可以发现,如果一个点 S S S(这里的点指缩点后的强联通分量)被其他点受欢迎,那么这个点的出度 = = = 0 0 0,这样才能保证其他点直接指向或者间接指向点 S S S,同时这个点不能指向其他点,且只能有一个出度为 0 0 0的。(大家可以画几个图验证一下)
最后的答案就是缩点后,出度为 0 0 0,且只有一个出度为0的强连通分量包含的结点数目。
需要注意,进行缩点前的边 i i i——> j j j,缩点后,边就为 s s c [ i ] ssc[i] ssc[i]——> s s c [ j ] ssc[j] ssc[j] s s c [ i ] ssc[i] ssc[i]表示第 i i i个点属于第 s s c [ i ] ssc[i] ssc[i]个强连通分量。

参考程序

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stack>
#include<cstring>
using namespace std;
int n,m,timee,low[10010],dfn[10010],h[10010];
int scc[10010],zh,du[10010];
int fist[100010],to[100010],nex[100010],t;//链表存图 
int s[10010],top,now[10010];//栈 
void add(int x,int y)
{
    to[++t]=y;
    nex[t]=fist[x];
    fist[x]=t;
    return;
}
int read(){ 		//快读
    int k=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}
    return k;
}
void tarjin(int u)
{
    low[u]=dfn[u]=++timee;
    s[++top]=u;
    now[u]=1;
    h[u]=1;
    int v;  
    for(int i=fist[u];i!=-1;i=nex[i])
    {
        v=to[i];
        if(h[v]==0)
        {
            tarjin(v);
            low[u]=min(low[u],low[v]);  
        }
        else if(now[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        zh++;
        do
        {
            v=s[top];
            top--;
            scc[v]=zh;//缩点 
            now[v]=0;
        }while(u!=v&&top>0);
         
    }
    return ;
}
int main()
{
    n=read();m=read();
    int x,y;
    memset(fist,-1,sizeof(fist));
    for(int i=1;i<=m;i++)
    {
        x=read();y=read();
        add(x,y);
    }
    for(int i=1;i<=n;i++)
        if(h[i]==0) tarjin(i);
    for(int i=1;i<=n;i++)
    {
        for(int j=fist[i];j!=-1;j=nex[j])
        {
            if(scc[i]!=scc[to[j]]) du[scc[i]]++;//求强联通分量的出度 
        }
    }
    int sum=0,ans;
    for(int i=1;i<=zh;i++)
    {
        if(!du[i]) {sum++;ans=i;}
    }
    if(sum==1)			//出度为0且只有一个有解
    {
        sum=0;
        for(int i=1;i<=n;i++)
        {
            if(scc[i]==ans) sum++;	//在出度为0的强连通分量里的所有结点都满足情况
        }
        printf("%d\n",sum);
    } 
    else
        printf("0");
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
USACO2022金组是国际在线判题系统USACO的最高级别,题目难度较高,在该比赛中取得好成绩是一项巨大的成就。以下是对该比赛的一些题目解析。 第一题:“交通计划” 题目要求:给定一个n个节点的有向图,每条边有一个长度,希望添加最少的边使得所有节点连通,求最小生成树的权值和。 解析:该题可以使用Kruskal算法求解,将每条边按权值从小到大排序,再依次加入,判断加入的边是否会形成环,若形成则不加入,直到所有节点连通为止。此时Kruskal算法得到的最小生成树的权值和即为所求。 第二题:“点火计划” 题目要求:给定一个n个节点的有向图,每条边有一个权值和一个点火时长,每个节点有一个点火启动时刻和时刻结束时刻,希望从其中选出一些边点火,使得所有节点都可从点火的边出发到达,且所选点火边的总点火时长最小。 解析:该题可以使用最小费用最大流算法求解。将每条边看做一个容量为1,费用为点火时长的边,源点向节点的点火边容量为1,费用为0的边,节点的点火边向汇点的容量为1,费用为0的边,对这个网络进行最小费用最大流即可得到所选边的总点火时长最小。 第三题:“美味佳肴” 题目要求:给定n个菜品,每个菜品有它的权值和两个类别,希望选出k个菜品,使得选出的菜品数量在每个类别中都不超过$\frac{k}{3}$个,且所选菜品的权值和最大。 解析:该题可以使用动态规划求解。设$f[i][j][k]$表示前i个菜品中,选择j个一类菜品,选择k个二类菜品的最大权值和,状态转移方程为$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][k]+a[i],f[i-1][j][k-1]+b[i])$,其中a[i]为i号菜品的权值,若为一类则为该权值,否则为0,b[i]为i号菜品的权值,若为二类则为该权值,否则为0。最终答案为$f[n][$k/3$][$k/3$]。 以上是对USACO2022金组的部分题目的解析,USACO比赛是全球范围内的计算机竞赛,竞争非常激烈,能够在该比赛中脱颖而出是一项非常棒的成就。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值