受欢迎的牛 洛谷P2341和POJ2186
题目背景
本题测试数据已修复。
题目描述
每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 AA 喜欢 BB,BB 喜欢 CC,那么 AA 也喜欢 CC。牛栏里共有 NN 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。
输入格式
第一行:两个用空格分开的整数:NN 和 MM。
接下来 MM 行:每行两个用空格分开的整数:AA 和 BB,表示 AA 喜欢 BB。
输出格式
一行单独一个整数,表示明星奶牛的数量。
输入输出样例
输入 #1
3 3 1 2 2 1 2 3
输出 #1
1
说明/提示
只有 33 号奶牛可以做明星。
【数据范围】
对于 10\%10% 的数据,N\le20N≤20,M\le50M≤50。
对于 30\%30% 的数据,N\le10^3N≤103,M\le2\times 10^4M≤2×104。
对于 70\%70% 的数据,N\le5\times 10^3N≤5×103,M\le5\times 10^4M≤5×104。
对于 100\%100% 的数据,1\le N\le10^41≤N≤104,1\le M\le5\times 10^41≤M≤5×104。
Popular Cows
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 46165 | Accepted: 18858 |
Description
Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.
Input
* Line 1: Two space-separated integers, N and M
* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.
* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.
Output
* Line 1: A single integer that is the number of cows who are considered popular by every other cow.
Sample Input
3 3 1 2 2 1 2 3
Sample Output
1
Hint
Cow 3 is the only cow of high popularity.
Source
package pro.proclass; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.StringTokenizer; // Targin + 缩点 public class P2341 implements Runnable { // head链式向前星数据结构 // dfn 图的访问顺序编号 // low 通过回边更新图的访问维护数组 // visit 标记强连通图的编号 // cntt[i] 记录强连通图编号为i的数量 // stk 存储强连通图中的点的容器 private static int[] head, dfn, low, visit, cntt, stk; // cnt 链式向前星的边的编号 // num 强连通图的编号个数 // seq 无向图的访问顺序 // index寻找强连通图中所有点的控制索引 private static int cnt, seq, num, index; // 可有可无,寻找到强连通图后,下次寻找允许其他元素链接上 private static boolean vis[]; private static Edge[] edges; private static void add(int u, int v) { edges[cnt] = new Edge(u, v, head[u]); // 构建链式向前星 head[u] = cnt++; } public static void main(String[] args) throws Exception { new Thread(null, new P2341(), "", 1 << 29).start(); } @Override public void run() { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = null; try { st = new StringTokenizer(br.readLine()); } catch (IOException e) { e.printStackTrace(); } int n = Integer.parseInt(st.nextToken()); int m = Integer.parseInt(st.nextToken()); edges = new Edge[m]; head = new int[n+1]; cntt = new int[n+1]; vis = new boolean[n+1]; stk = new int[n+1]; Arrays.fill(head, -1); // 初始化链式向前星head for (int i = 0; i < m; i++) { try { st = new StringTokenizer(br.readLine()); } catch (IOException e) { e.printStackTrace(); } int a = Integer.parseInt(st.nextToken()); int b = Integer.parseInt(st.nextToken()); add(a, b); } dfn = new int[n+1]; low = new int[n+1]; visit = new int[n+1]; seq = 0; index = 0; for (int i = 1; i <= n; i++) { if(dfn[i] == 0) tarjan(i); } int[] out = new int[num+1]; for (int i = 0; i < m; i++) { int fr = edges[i].fr; int to = edges[i].to; if(visit[fr] != visit[to]) { // 如果一条边的两个端点不在同一个强连通图中(缩点) 给出度+1 out[visit[fr]]++; } } int counts = 0; int ans = 0; for (int i = 1; i <= num; i++) { if(out[i] == 0) { // 如果一个图中,出度为0 只有一个,那么这个圈中的所有点都是被其他牛仰慕的 counts++; ans = cntt[i]; } } if(counts != 1) System.out.println(0); else System.out.println(ans); } private static void tarjan(int x) { dfn[x] = low[x] = ++seq; stk[++index] = x; vis[x] = true; for (int i = head[x]; i != -1 ; i = edges[i].next) { int to = edges[i].to; if(dfn[to] == 0) { tarjan(to); low[x] = Math.min(low[x], low[to]); } else if(vis[to])low[x] = Math.min(low[x], dfn[to]); } if(low[x] == dfn[x]) { // 当low == dfn时说明回溯到头,这个区域内是强连通图 num += 1; // 强连通图的编号 while (true) { int cur = stk[index]; // 当前最后一个点 index--; // 循环回退 vis[cur] = false; // 标记未被访问 visit[cur] = num; // 标记当前点的强连通编号 cntt[num]++; // 标记当前强连通编号点的个数 if(cur == x) break; // 当回退到最开始的点x时 跳出循环 } } } static class Edge { int fr; int to; int next; public Edge(int fr, int to, int next) { this.fr = fr; this.to = to; this.next = next; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理