Acwing4244牛的比赛

Acwing4244.牛的比赛

题目部分

N 头奶牛,编号 1∼N,一起参加比赛。

奶牛的战斗力两两不同。

这些奶牛之间已经进行了 M轮两两对决。

在对决中,战斗力高的奶牛一定会战胜战斗力低的奶牛。

请问,通过上述 M轮对决的结果,可以确定多少头奶牛的具体战斗力排名。

输入格式

第一行包含两个整数 N ,M

接下来 M 行,每行包含两个整数 a ,b ,表示奶牛 a 和奶牛 b 之间进行了对决,并且奶牛 a 战胜了奶牛 b

输出格式

输出可以确定具体战斗力排名的奶牛数量。

数据范围

1≤N ≤100,

1≤M ≤4500,

数据保证合法。

输入样例:

5 5
4 3
4 2
3 2
1 2
2 5

输出样例:

2

样例解释

2号奶牛输给了 1,3,4 号奶牛,战胜了 5 号奶牛,可以确定它的战斗力排名为 4。

5号奶牛输给了排在第 4 的 2 号奶牛,所以它的战斗力排名为 5。

其它奶牛不确定。

解法

首先这道题,虽然大佬们说是板子题,但是对我图论新手来说,这题还是相当有意思的!学到了很多!

错误解法

首先原先的我,是有点懵的,在看到这道题时,我们能够通过floyd得出这道题?我是怎么也想不出

我只能分析出某头牛的排名能确定下来,是因为它间接和其余的牛battle过

然后看了下大佬的题解(仅文字部分):

这是link,才做出了错误解法

还原下,我做出错误解法的思路哈:

我们可以把这些两头牛有没有间接比较过,转换为每个牛都是对应图中的节点,然后两点中可达,则为间接比较过

由此我通过转换关系把题目图化了,但是我不是添加单条有向边,而是添加了双向边

赋值为1的,代表着战胜

赋值为0的,代表着战败

想得很美好,由于对floyd算法运行流程不熟悉,导致误以为只要判断通过战败可达其余点或者战胜可达其余点即可,可是在floyd过程中,却是我们可以先通过战败边到另一个点,然后再由这个点选择战胜边接着刷新dist数组,导致算法有问题

java 复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author:天才玩家M
 * @date:2023/11/29 16:05
 * @description:TODO
 */
public class Main {
    static int n,m;
    static int N=110;
    static int INF=Integer.MAX_VALUE/4;
    static int [][]dist=new int[N][N];
    public static void floyd(){
        for (int k = 1; k <=n; k++) {
            for (int i = 1; i <=n; i++) {
                for (int j = 1; j <=n ; j++) {
                    dist[i][j]=Math.min(dist[i][j],dist[i][k]+dist[k][j]);
                }
            }
        }
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        String[] s1 = s.split(" ");
        n = Integer.parseInt(s1[0]);
        m = Integer.parseInt(s1[1]);
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <=n; j++) {
                if(i==j){
                    dist[i][j]=1;
                }else{
                    dist[i][j]=INF;
                }
            }

        }
        for (int i = 0; i < m; i++) {
            s = br.readLine();s1 = s.split(" ");
            int a=Integer.parseInt(s1[0]);
            int b=Integer.parseInt(s1[1]);
            dist[a][b]=1;//代表可达且胜利
            //dist[b][a]=0;不能多出这一条,这只能是a->b单向的
            //不对上面的说法是错的,我们可以设置可达不过要与胜利有所区别
            dist[b][a]=0;//代表失败的可达,
            //想法很好,可是你有没有想过我们失败的点可以沿着失败的路线上去到达某个成功的点然后由成功的点再蔓延下去呢?
            //这种情况就违背了我们最初的意愿了,只让他们沿着成功之路或者失败之路而上,且我们只统计成功之路以及失败之路
            //2:可是这种情况下,如果夹杂成成功与失败,那我们又不会去统计!!!
            //那为什么正确答案为6而我们的为2呢?
            //解决了,答案浮出水面了,就是因为不去统计,假如1已经确定是倒数第一,而你唯一赢他的你又不算,那这不就少了吗?
        }
        floyd();
        int res=0;
        for (int i = 1; i <=n; i++) {
            int count1=0;
            int count=0;
            for (int j = 1; j <=n; j++) {
                if(dist[i][j]==0){
                    count1++;
                }
                else if(dist[i][j]!=INF){
                    count++;
                }
            }
            if(count==n||count1==n){
                res++;
            }
        }
        System.out.println(res);
    }
}

正确做法

大佬优雅地解决我一直困扰的战败与战胜,通过仅赋值单向边,而之后同时判断if(dist[i][j]<=INF/2||dist[j][i]<=INF/2),来确定这个对手是否交战过,并且由于仅赋值了战胜那一条,目前点要么战胜要么战败,而且看得比我的透彻,其实做出错误做法时,我就已经被要么战胜要么战败给迷惑了,

可是实际情况下是只要战过,无论成败即知排名(挺好想的,只要交手过,赢你的在前,输你的在后,你一定可以找到个位置).

至于INF/2是由于边比较多设定的,其他的也行

java 复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author:天才玩家M
 * @date:2023/11/29 20:38
 * @description:TODO
 */
public class Main1 {
    static int n,m;
    static int N=110;
    static int INF=Integer.MAX_VALUE/2;
    static int [][]dist=new int[N][N];
    public static void floyd(){
        for (int k = 1; k <=n; k++) {
            for (int i = 1; i <=n; i++) {
                for (int j = 1; j <=n ; j++) {
                    dist[i][j]=Math.min(dist[i][j],dist[i][k]+dist[k][j]);
                }
            }
        }
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        String[] s1 = s.split(" ");
        n = Integer.parseInt(s1[0]);
        m = Integer.parseInt(s1[1]);
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <=n; j++) {
                if(i==j){
                    dist[i][j]=1;
                }else{
                    dist[i][j]=INF;
                }
            }

        }
        for (int i = 0; i < m; i++) {
            s = br.readLine();s1 = s.split(" ");
            int a=Integer.parseInt(s1[0]);
            int b=Integer.parseInt(s1[1]);
            dist[a][b]=1;
        }
        floyd();
        int res=0;
        for (int i = 1; i <=n; i++) {
          int count=0;
            for (int j = 1; j <=n; j++) {
                if(dist[i][j]<=INF/2||dist[j][i]<=INF/2){
                    count++;
                }
            }
            if(count==n){
                res++;
            }
        }
        System.out.println(res);
    }
}
相关推荐
诚丞成7 分钟前
滑动窗口篇——如行云流水般的高效解法与智能之道(1)
算法
带多刺的玫瑰2 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
爱敲代码的憨仔2 小时前
《线性代数的本质》
线性代数·算法·决策树
yigan_Eins2 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
阿史大杯茶2 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
დ旧言~3 小时前
【高阶数据结构】图论
算法·深度优先·广度优先·宽度优先·推荐算法
张彦峰ZYF3 小时前
投资策略规划最优决策分析
分布式·算法·金融
The_Ticker3 小时前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
爪哇学长3 小时前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
Dola_Pan4 小时前
C语言:数组转换指针的时机
c语言·开发语言·算法