洛谷p2853Cow picnic S题解

题目描述

The cows are having a picnic! Each of Farmer John's K (1 ≤ K ≤ 100) cows is grazing in one of N (1 ≤ N ≤ 1,000) pastures, conveniently numbered 1...N. The pastures are connected by M (1 ≤ M ≤ 10,000) one-way paths (no path connects a pasture to itself).

The cows want to gather in the same pasture for their picnic, but (because of the one-way paths) some cows may only be able to get to some pastures. Help the cows out by figuring out how many pastures are reachable by all cows, and hence are possible picnic locations.

K(1≤K≤100) 只奶牛分散在 N(1≤N≤1000) 个牧场.现在她们要集中起来进餐。牧场之间有 M(1≤M≤10000) 条有向路连接,而且不存在起点和终点相同的有向路.她们进餐的地点必须是所有奶牛都可到达的地方。那么,有多少这样的牧场可供进食呢?

输入格式

Line 1: Three space-separated integers, respectively: K, N, and M

Lines 2..K+1: Line i+1 contains a single integer (1..N) which is the number of the pasture in which cow i is grazing.

Lines K+2..M+K+1: Each line contains two space-separated integers, respectively A and B (both 1..N and A != B), representing a one-way path from pasture A to pasture B.

输出格式

Line 1: The single integer that is the number of pastures that are reachable by all cows via the one-way paths.

输入输出样例

输入 #1复制

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

输出 #1复制

2

说明/提示

The cows can meet in pastures 3 or 4.

思路:

dfs+循环

通过暴力枚举每一个牧场,再看一看是不是所有牛都可以来到这个牧场。

存图可以用vector动态数组。

#include <bits/stdc++.h>
using namespace std;
long long k,n,m,ans,t[1010],a[1010],vis[1010],x,y;
vector<int>b[1010];
void dfs(int x){
     vis[x]=1;  
     t[x]++;
     for(int i=0;i<b[x].size();i++)
         if(!vis[b[x][i]])
             dfs(b[x][i]);
}
int main(){
    cin>>k>>n>>m;
    for(int i=1;i<=k;i++)cin>>a[i];
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        b[x].push_back(y);
    }
    for(int i=1;i<=k;i++){
        for(int j=1;j<=n;j++) vis[j]=0;  
        dfs(a[i]);
    }
    for(int i=1;i<=n;i++) if(t[i]==k) ans++;
    cout<<ans;
}

t数组是存储一个牧场它被每个奶牛经历的次数,只要次数=奶牛总数,就ans数量+1

<think>嗯,我现在需要解决上的题目P2853。好吧,首先我得仔细看一下题目要求。题目名称是“Cow Picnic”,也就是牛的野餐。题目大意是说,有K头牛分散在不同的牧场,牧场之间是有向路连接的。我们需要找出所有牛都能到达的那些牧场,然后输出这些牧场的数量。对吗? 那输入是什么样的呢?我记得输入的第一行是三个整数,K、N、M,分别代表牛的数量、牧场的数量和道路的数量。然后是K个整数,表示每头牛所在的牧场编号。接下来是M条有向道路,每条道路给出起点和终点。我们的任务是统计所有牛都能到达的牧场的总数。 首先,我得想,对于每一头牛所在的牧场,我需要找出它能到达的所有牧场。然后,找到这些集合的交集,也就是所有牛都共同能到达的牧场数目。这应该就是最终答案了。 那具体怎么做呢?可能的思路是,对于每头牛所在的牧场,进行一次遍历,比如深度优先搜索(DFS)或者广度优先搜索(BFS),标记它能到达的所有牧场。然后,统计每个牧场被访问的次数,如果某个牧场被所有K头牛访问过,那么它就是一个符合要求的牧场,最后统计这样的牧场数量。 举个例子,比如K=2头牛分别在牧场1和牧场2。假设牧场1能到达3、4,牧场2能到达3、5。那么共同能到达的只有牧场3,所以答案是1。 那具体实现的话,可能需要一个数组或者二维数组来记录每个牧场被每头牛访问的情况。例如,可以用一个二维数组count[K][N],其中count[i][j]表示第i头牛是否能够到达牧场j。但这样的话,当K很大的时候,可能会比较占内存。不过题目中的N最大是1000,K最多是100,所以二维数组的大小是100*1000=1e5,应该是可以的。 或者,更优化的方式是,对于每个牧场,维护一个计数器,记录有多少头牛能够到达它。当这个计数器的值等于K时,就将该牧场计入结果。这种方法可能更节省空间,因为只需要一个一维数组,每个牧场的位置存储被多少头牛访问过。 那具体步骤应该是这样的: 1. 读取输入:K,N,M,然后读取K个牛的初始位置。接着读取M条有向边。 2. 构建图的结构:因为牧场之间是有向边,所以需要建立一个邻接表,每个节点指向它能到达的节点。例如,用邻接表存储,每个牧场i对应的列表是它能直接到达的牧场。 3. 对于每一头牛所在的起点,进行遍历(BFS或DFS),在遍历过程中,将能到达的牧场在该牛的记录中标记。同时,每访问一个牧场,就将该牧场的计数器加一。例如,维护一个数组cnt[N+1],初始化为0。当一头牛访问到某个牧场时,cnt[该牧场]++。这样,当所有牛都处理完后,遍历所有牧场,统计cnt[i]等于K的数量。 不过,这样做的话,每次处理一头牛的时候,都需要记录该牛访问过的牧场,然后将这些牧场的计数器加一。或者,可以换一种方式,每次处理一头牛时,遍历它能到达的所有牧场,并将这些牧场的计数器加一。比如,对于第i头牛,从它的起始位置出发,进行BFS/DFS,找到所有可达的牧场,并将这些牧场的计数器加一。最后,统计计数器等于K的牧场数目。 是的,这样的话,每个牧场的计数器记录了有多少头牛可以到达它。最终,计数器等于K的牧场数目就是答案。这应该是一个可行的方法。 那具体如何实现呢?例如,邻接表的建立。假设N是牧场数目,每个牧场编号从1到N。邻接表可以用一个数组,每个元素是一个列表或者向量,存储该节点的所有出边指向的节点。 然后,对于每头牛的位置s,进行BFS或者DFS,将所有能到达的节点标记,并增加它们的计数器。比如,初始化一个访问数组visited,每次遍历时,标记该牛能到达的节点,每访问一个节点,就将该节点的计数器加一。但要注意,同一头牛多次访问同一个节点的话,只能算一次。所以,在遍历的时候,要确保每个节点在每头牛的遍历过程中只被处理一次。 例如,处理一头牛时,初始位置是s。用BFS的话,队列中的节点会被访问一次,并标记为已访问。这样,所有可达的节点都会被处理,并且每个节点在这头牛的遍历中只会被访问一次。然后,在处理过程中,每访问一个节点u,就将cnt[u]++。这样,处理完所有牛之后,遍历所有牧场,统计cnt[u] == K的数量。 这应该就是正确的做法了。那么,现在需要考虑的是如何高效地实现这个过程。因为K可以达到100,N到1000,每个BFS的时间复杂度是O(N+M),所以总的时间复杂度是K*(N+M)。例如,当K=100,N=1000,M=10000,那么总共有100*(1000+10000)=1,100,000次操作,这在时间上是可行的,因为通常1e8次操作才可能超时,所以这个规模应该没问题。 那现在需要考虑的是代码的结构。例如,在C++中,可以用邻接表vector<vector<int>> adj(N+1),其中adj[u]存储u能到达的v的列表。然后,对于每个牛的起始点s,进行BFS或者DFS,并记录访问过的节点,每访问一个节点,就将cnt[u]++。 注意,每头牛的遍历必须独立,不能和其他牛的遍历共享访问数组。也就是说,每次处理一头牛的时候,需要有一个新的visited数组,或者每次处理完一头牛后清空visited数组。或者,在每次处理一头牛时,使用一个本地的访问数组,来确保不会重复访问节点。 例如,在每次处理一头牛的时候,初始化一个visited数组,初始化为false。然后进行BFS,每次访问一个节点u时,如果visited[u]为false,就标记为true,并将cnt[u]++。这样,同一头牛的遍历不会重复访问同一个节点,而不同牛的遍历之间不会互相干扰。 所以,具体的代码流程大概是这样的: 初始化邻接表adj。 初始化cnt数组为0。 对于每头牛的起始位置s: 初始化visited数组为false。 进行BFS或者DFS,从s出发,访问所有可达的节点: 对于每个访问到的节点u: 如果visited[u]为false: visited[u] = true cnt[u] +=1 这样处理完所有牛之后,遍历所有1到N的节点,统计cnt[u] == K的数量,即为答案。 那这个思路应该是对的。那现在要考虑的是如何处理输入,以及具体的实现细节。 比如,输入中的K、N、M。然后接下来的一行是K个整数,表示每头牛所在的牧场编号。然后M行,每行两个整数A和B,表示存在一条有向边A->B。 需要注意的问题: 1. 牧场的编号是从1到N吗?题目中的输入应该如此,因为通常题目中的节点编号都是1-based的。 2. 重复的边如何处理?比如,同一条边可能出现多次。邻接表可以直接存储,因为BFS或DFS处理的时候,重复边不会影响结果,因为一旦访问过节点,就会标记为已访问,不再处理。所以邻接表中可以有重复的边,不影响正确性,但可能会影响效率。不过题目中的M可能很大,但通常邻接表不会去重,因为题目中的边可能有重复,或者不需要处理。 但题目中给出的道路是M条,可能并没有重复的边,或者题目中允许重复边。无论怎样,邻接表中保留所有边是正确的做法,因为存在多条边的话,可能不同的路径到达同一个节点,但无论如何,只要处理每个节点一次即可。 所以邻接表的构造没有问题。 那现在,举个例子来验证一下思路是否正确。 例如,样例输入: 2 4 4 2 3 1 2 1 4 2 3 3 1 K=2,N=4,M=4。牛分别在牧场2和牧场3。 邻接表: 1->2, 4 2->3 3->1 其他的节点可能没有出边?比如,4的出边可能没有。 然后,处理第一个牛,起始点是2。BFS过程: 初始队列是2。标记visited[2]=true,cnt[2] +=1。然后处理它的邻居3。将3加入队列,标记visited[3]=true,cnt[3]+=1。然后处理3的邻居1,标记visited[1]=true,cnt[1]+=1。然后处理1的邻居2(已访问)和4。将4加入队列,标记visited[4]=true,cnt[4]+=1。4没有出边,处理完毕。所以这头牛能访问到2、3、1、4,共4个牧场。所以这四个牧场的cnt各加1。 处理第二头牛,起始点是3。BFS初始队列是3。标记visited[3]=true,cnt[3]+=1。处理3的邻居1,标记visited[1]=true,cnt[1]+=1。处理1的邻居2和4。将2加入队列,标记visited[2]=true,cnt[2]+=1。处理2的邻居3(已访问)。处理4的出边(假设没有),所以队列处理完毕。所以这头牛能访问到3、1、2、4。这四个牧场的cnt各加1。 现在,每个牧场的cnt是: 1: 被两次访问(两个牛都访问到)→ cnt=2 2: 两次 → cnt=2 3: 两次 → cnt=2 4: 两次 → cnt=2 所以所有这四个牧场都被两个牛访问到了。但是样例的输出是?原题中的样例输入可能不是这个情况。或者可能我的例子不是原题中的样例。或者可能我在这里搞错了。原题中的样例可能输出是某个数值。但根据这个例子,答案应该是4? 或者可能我举的例子有问题。那原题中的样例是怎样的呢?比如,原题中的样例输入: 例如,P2853的样例输入可能是: 2 4 4 2 3 1 2 1 4 2 3 3 1 那么,第一个牛在2,第二个在3。 第一个牛的访问路径是:2→3→1→4。所以这四个牧场都被访问到。 第二个牛的访问路径是:3→1→2→4。所以这四个牧场也被访问到。所以所有四个牧场都被两个牛访问到了。所以答案应该是4? 但是原题的样例输出可能不是这个结果。或者可能我哪里搞错了? 或者,可能原题的样例输入不同。可能我需要重新思考。 或者,原题的正确样例输出是某个特定的数值。比如,假设样例的输出是2,那可能我的思路有问题。但此时可能需要重新分析问题。 或者,可能我的例子中的两个牛都能到达所有四个牧场,所以答案应该是4?或者可能某个牧场无法被两个牛都到达? 或者,可能我的例子中的情况是正确的,但原题的样例可能不同。比如原题中的样例可能给出不同的结果。比如,假设原题的样例输入是: 比如,假设输入是: 2 4 4 2 3 1 2 1 4 2 3 3 1 那根据上述分析,答案是4? 或者可能原题的样例输入是另一个情况。不管怎样,按照题目要求,我们需要正确实现算法。 那回到题目,假设我的思路是正确的,现在需要编写代码。那么如何实现呢? 例如,在C++中,可以使用邻接表,然后对每个牛的位置进行BFS,并记录访问情况,并更新cnt数组。 代码的大致结构: #include <iostream> #include <vector> #include <queue> using namespace std; int main() { int K, N, M; cin >> K >> N >> M; vector<int> cows(K); for (int i = 0; i < K; ++i) { cin >> cows[i]; } vector<vector<int>> adj(N + 1); // 1-based索引 for (int i = 0; i < M; ++i) { int A, B; cin >> A >> B; adj[A].push_back(B); // 有向边A->B } vector<int> cnt(N + 1, 0); // 记录每个牧场被多少牛访问过 for (int s : cows) { vector<bool> visited(N + 1, false); queue<int> q; q.push(s); visited[s] = true; cnt[s]++; // 起始点自己也要算一次? while (!q.empty()) { int u = q.front(); q.pop(); for (int v : adj[u]) { if (!visited[v]) { visited[v] = true; cnt[v]++; q.push(v); } } } } int res = 0; for (int i = 1; i <= N; ++i) { if (cnt[i] == K) { res++; } } cout << res << endl; return 0; } 这里需要注意的是,当处理一头牛的起始点s时,在BFS过程中,每次访问到节点v时,就将cnt[v]加一。例如,起始点s自己会被访问到,所以cnt[s]也会加一。例如,当某个牧场是多个牛的起始点时,那么每个牛访问自己的时候,都会让cnt[s]增加。这应该没有问题,因为每个牛确实能到达自己的起始点。 比如,假设有两头牛都在牧场2,那么牧场2的cnt会被加两次,所以当K=2时,牧场2的cnt会是2,满足条件。 那这样是否正确? 是的。比如,每个牛的起始点自己都能被该牛到达,所以会被计入cnt。这样处理是正确的。 那这个代码是否考虑到了所有情况? 比如,是否有环的情况?例如,某个牧场形成环,比如A→B→C→A。在BFS中,已经访问过的节点不会再处理,所以不会出现无限循环。所以这个处理是正确的。 那这样的话,这个代码应该能正确处理题目中的所有情况。 现在,测试一下样例。假设原题的样例输入是: 样例输入: 2 4 4 2 3 1 2 1 4 2 3 3 1 那么根据代码处理: 第一头牛是2。从2出发,BFS队列: 初始:队列有2。visited[2]=true,cnt[2] +=1 → cnt[2]=1. 处理2的出边:3。未被访问。visited[3]=true,cnt[3] +=1 → cnt[3]=1。入队。 处理3的出边:1。未被访问。visited[1]=true,cnt[1] +=1 → cnt[1]=1。入队. 处理1的出边:2和4。2已被访问。4未被访问。visited[4]=true,cnt[4] +=1 → cnt[4]=1。入队. 队列现在有3、1、4。依次处理: 处理3:出队,无更多边。 处理1:出队,检查出边中的4是否已访问?是的,因为4已经被处理? 或者,原邻接表中的顺序可能影响访问顺序,但不会影响结果。例如,假设邻接表中1的出边是2和4,那么处理1的时候,会先处理2,已经被访问,然后处理4,未被访问,标记为已访问,入队。此时队列中还有3、1、4。处理完1后,队列中的下一个是4。处理4的出边,假设没有,则队列为空。 此时,这头牛处理完毕。此时,cnt[2]=1, cnt[3]=1, cnt[1]=1, cnt[4]=1。 第二头牛是3。BFS队列初始为3。visited[3]=true,cnt[3] +=1 → cnt[3]=2. 处理3的出边1。未被访问。标记为true,cnt[1] +=1 → cnt[1]=2.入队. 处理1的出边:2和4。2未被当前牛访问过吗?此时visited数组是新的,初始都为false。所以2未被访问。所以标记2为true,cnt[2] +=1 → cnt[2]=2。入队。然后处理4:未被访问。标记为true,cnt[4] +=1 → cnt[4]=2。入队. 处理2的出边:3,已被访问。处理4的出边:无。 队列此时处理完毕。此时,这头牛的访问情况是3、1、2、4。他们的cnt各加一。 现在,四个牧场的cnt都是2。因此,最终结果是4,符合我们的预期。 那这说明代码是正确的? 但是原题的样例输出是否如此呢?比如,假设原题的样例输入对应的输出是2,则说明哪里有问题? 或者可能原题的样例输入是不同的。例如,可能原题的样例输入是: 假设原题的样例输入是: 2 4 4 2 3 1 2 1 4 2 3 3 4 那么在这种情况下,第二个牛从3出发,能到达的节点是3→4,所以第一头牛能到达的节点是2→3→4(假设边是3→4的话?或者原样例可能不同)。 这时候需要重新计算。所以需要确定原题的正确样例。或者,可能我在这里的思考中样例的输出应该是正确的。 总之,按照这个思路,代码应该可以正确处理各种情况。现在,我需要确认代码是否存在可能的错误。 可能的错误点: 1. 初始化邻接表是否正确?是的,每个边是有向的,所以应该添加A→B到adj[A]中。 2. 是否处理了所有牛的位置?是的,循环遍历每个牛的起始点。 3. 在BFS过程中,是否将每个可达的节点正确标记,并增加cnt?是的,每次访问一个新节点时,cnt[v]++。 4. 是否每个牛只能访问每个节点一次?是的,visited数组是每次BFS独立的,每次重新初始化为false,因此同一头牛的遍历不会重复访问节点。 5. 是否所有可能的节点都被处理?例如,节点可能没有出边,但BFS仍然会处理该节点,因为该节点是可达的。 所以,代码应该正确。 因此,这道题的解法是正确的,可以通过所有测试用例。</think>要解决题目P2853“牛的野餐”,我们需要确定所有牛都能到达的牧场数目。以下是详细的解决方法: ### 题目分析 - **输入**:K头牛分布在N个牧场,M条有向道路。 - **目标**:找出所有牛都能到达的牧场数量。 ### 方法思路 1. **图的构建**:使用邻接表存储牧场之间的有向道路。 2. **遍历每个牛的路径**:对每头牛所在的牧场进行广度优先搜索(BFS)或深度优先搜索(DFS),记录它能到达的所有牧场。 3. **统计共同可达的牧场**:维护一个计数器数组,记录每个牧场被多少头牛访问过。最终,计数器等于牛的数量K的牧场即为答案。 ### 解决代码 ```cpp #include <iostream> #include <vector> #include <queue> using namespace std; int main() { int K, N, M; cin >> K >> N >> M; vector<int> cows(K); for (int i = 0; i < K; ++i) { cin >> cows[i]; } vector<vector<int>> adj(N + 1); for (int i = 0; i < M; ++i) { int A, B; cin >> A >> B; adj[A].push_back(B); } vector<int> cnt(N + 1, 0); for (int s : cows) { vector<bool> visited(N + 1, false); queue<int> q; q.push(s); visited[s] = true; cnt[s]++; while (!q.empty()) { int u = q.front(); q.pop(); for (int v : adj[u]) { if (!visited[v]) { visited[v] = true; cnt[v]++; q.push(v); } } } } int res = 0; for (int i = 1; i <= N; ++i) { if (cnt[i] == K) { res++; } } cout << res << endl; return 0; } ``` ### 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值