P3119 [USACO15JAN]草鉴定Grass Cownoisseur

本文介绍了一道USACO竞赛题目“草鉴定GrassCownoisseur”的解题思路,通过Tarjan算法进行图的缩点处理,并利用拓扑排序找出奶牛贝西在遵循或违背路径方向情况下能访问的最大不重复草场数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                               [USACO15JAN]草鉴定Grass Cownoisseur

 

题目描述

In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-way cow paths all over his farm. The farm consists of N fields, conveniently numbered 1..N, with each one-way cow path connecting a pair of fields. For example, if a path connects from field X to field Y, then cows are allowed to travel from X to Y but not from Y to X.

Bessie the cow, as we all know, enjoys eating grass from as many fields as possible. She always starts in field 1 at the beginning of the day and visits a sequence of fields, returning to field 1 at the end of the day. She tries to maximize the number of distinct fields along her route, since she gets to eat the grass in each one (if she visits a field multiple times, she only eats the grass there once).

As one might imagine, Bessie is not particularly happy about the one-way restriction on FJ's paths, since this will likely reduce the number of distinct fields she can possibly visit along her daily route. She wonders how much grass she will be able to eat if she breaks the rules and follows up to one path in the wrong direction. Please compute the maximum number of distinct fields she can visit along a route starting and ending at field 1, where she can follow up to one path along the route in the wrong direction. Bessie can only travel backwards at most once in her journey. In particular, she cannot even take the same path backwards twice.

约翰有n块草场,编号1到n,这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。

贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经过多次。因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。

输入输出格式

输入格式:

 

INPUT: (file grass.in)

The first line of input contains N and M, giving the number of fields and the number of one-way paths (1 <= N, M <= 100,000).

The following M lines each describe a one-way cow path. Each line contains two distinct field numbers X and Y, corresponding to a cow path from X to Y. The same cow path will never appear more than once.

 

输出格式:

 

OUTPUT: (file grass.out)

A single line indicating the maximum number of distinct fields Bessie

can visit along a route starting and ending at field 1, given that she can

follow at most one path along this route in the wrong direction.

 

输入输出样例

输入样例#1:
7 10 
1 2 
3 1 
2 5 
2 4 
3 7 
3 5 
3 6 
6 5 
7 2 
4 7 

输出样例#1:
6 

说明

SOLUTION NOTES:

Here is an ASCII drawing of the sample input:

v---3-->6

7 |\ |

^\ v \ |

| \ 1 | | | v | v 5

4<--2---^

Bessie can visit pastures 1, 2, 4, 7, 2, 5, 3, 1 by traveling

backwards on the path between 5 and 3. When she arrives at 3 she

cannot reach 6 without following another backwards path.

 

题意就是给你一张单向边的图,从1出发,问你最多能走过几个点再回到1,一个点可以重复走,但只算一次,还有一次走逆向边的机会

看完题目,我们发现图中有环,显而易见,先tarjan缩点,建一个新图,

如果不走逆向边,那么从1出发回到1,新图中没有环时,走过的点数就是1所在的环点的数目

有环时就是新环中点的数目

但是我们又一次走逆向边的机会,所以在没有环的情况下,我们可以主动制造一个环

 

就如上面的情况,我们把 1->2 反向就成了一个环

就目前状态来看,我们可以认为这是由环断为两条链 

链中存在两种点 一个是1可以到达的点 

另一种是可以到达1的点

用拓扑排序处理 1能到达的点和 能到1的点

显然 我们可以枚举某一条边,把它反向,ans=从1到此边一点的数目+另一点到1的点的数目-1所在环点的数目(这个环加了两次)

  1 #include <cctype>
  2 #include <cstdio>
  3 
  4 const int MAXN=100010;
  5 const int INF=0x3f3f3f3f;
  6 
  7 int n,m,top,id,inr,ans;
  8 
  9 int dfn[MAXN],low[MAXN],stack[MAXN],belong[MAXN],sum[MAXN];
 10 
 11 bool vis[MAXN];
 12 
 13 struct node {
 14     int to;
 15     int next;
 16     node(){}
 17     node(int to,int next):to(to),next(next){}
 18 };
 19 node e[MAXN];
 20 
 21 int head[MAXN],tot;
 22 
 23 inline void read(int&x) {
 24     int f=1;register char c=getchar();
 25     for(x=0;!isdigit(c);c=='-'&&(f=-1),c=getchar());
 26     for(;isdigit(c);x=x*10+c-48,c=getchar());
 27     x=x*f;
 28 }
 29 
 30 inline int max(int a,int b) {return a<b?b:a;}
 31 
 32 inline void add(int x,int y) {
 33     e[++tot]=node(y,head[x]);
 34     head[x]=tot;
 35 }
 36 
 37 inline int min(int a,int b) {return a<b?a:b;}
 38 
 39 void tarjan(int u) {
 40     dfn[u]=low[u]=++inr;
 41     vis[u]=true;
 42     stack[++top]=u;
 43     for(int i=head[u];i;i=e[i].next) {
 44         int v=e[i].to;
 45         if(!dfn[v]) {
 46             tarjan(v);
 47             low[u]=min(low[u],low[v]);
 48         }
 49         else if(vis[v]) low[u]=min(low[u],dfn[v]);
 50     }
 51     if(dfn[u]==low[u]) {
 52         ++id;
 53         int t;
 54         do {
 55             t=stack[top--];
 56             vis[t]=false;
 57             belong[t]=id;
 58             ++sum[id];
 59         }while(u!=t);
 60     }
 61     return;
 62 }
 63 
 64 struct Topu_sort{
 65     struct p{
 66         int to;
 67         int next;
 68     };
 69     p edge[MAXN];
 70     int Head[MAXN],Tot;
 71     int in[MAXN],q[MAXN],f[MAXN];
 72     int h,t;
 73     void add(int x,int y) {
 74         ++in[y];
 75         edge[++Tot].to=y;
 76         edge[Tot].next=Head[x];
 77         Head[x]=Tot;
 78     }
 79     void Sort() {
 80         for(int i=1;i<=id;++i) f[i]=-INF;
 81         f[belong[1]]=sum[belong[1]];
 82         for(int i=1;i<=id;++i) 
 83           if(!in[i]) q[++t]=i;
 84         while(h<t) {
 85             int u=q[++h];
 86             for(int i=Head[u];i;i=edge[i].next) {
 87                 int v=edge[i].to;
 88                 f[v]=max(f[v],f[u]+sum[v]);
 89                 if(!--in[v]) q[++t]=v;
 90             }
 91         }
 92     }
 93 }pos,neg;
 94 
 95 int hh() {
 96     int x,y;
 97     read(n);read(m);
 98     for(int i=1;i<=m;++i) {
 99         read(x);read(y);
100         add(x,y);
101     }
102     for(int i=1;i<=n;++i) 
103       if(!dfn[i]) tarjan(i);
104     for(int i=1;i<=n;++i) 
105       for(int j=head[i];j;j=e[j].next) {
106           int to=e[j].to;
107           if(belong[i]!=belong[to]) {
108               pos.add(belong[i],belong[to]);
109               neg.add(belong[to],belong[i]);
110           } 
111       }
112     pos.Sort();
113     neg.Sort();
114     ans=sum[belong[1]];
115     for(int i=1;i<=n;++i)
116       for(int j=head[i];j;j=e[j].next) {
117           int v=e[j].to;
118           ans=max(ans,pos.f[belong[v]]+neg.f[belong[i]]);
119       }
120     printf("%d\n",ans-sum[belong[1]]);
121     return 0;
122 } 
123 
124 int sb=hh();
125 int main(int argc,char**argv) {;}
代码

 

### USACO P2035 iCow 题目解析 #### 问题描述 Farmer John 购买了一台新的 MP3 播放器 iCow,其中存储了 N (1 ≤ N ≤ 1,000) 首歌曲,每首歌都有一个初始权值 Ri (1 ≤ Ri ≤ 10,000)[^4]。播放顺序由 FJ 设计的独特算法决定: - 下一首播放的是当前所有未播放过的歌曲中权值最高的那一首;若有多个最高权值,则选择编号最小的一首。 - 当某首歌曲播放完成后,其权值会均匀分配给其余 N − 1 首歌曲,并将其自身的权值设为零。 - 如果该歌曲的权值不能被 N − 1 整除,则剩余部分将以 1 单位的形式依次给予排名靠前但尚未获得额外权重的歌曲。 任务是求出按照上述规则最先播放的 T (1 ≤ T ≤ 1000) 首歌曲的具体情况。 #### 解决方案思路 为了模拟这个过程并找到最开始播放的 T 首歌曲,可以采用优先队列(最大堆)来管理待播列表及其对应的权重。每次取出具有最大权重的元素作为即将播放的对象,在更新其他成员的新权重之后重新加入到队列当中继续循环直至达到所需次数为止。 具体步骤如下: - 初始化数据结构:创建一个包含所有歌曲 ID 和它们各自起始分数的最大堆; - 输出此目标的信息; - 更新剩余项目的得分并将已处理项移回至集合内等待下次轮转; 下面给出完整的 C++ 实现代码示例: ```cpp #include <iostream> #include <queue> using namespace std; struct Song { int id; long score; }; bool operator<(const Song& a, const Song& b){ return !(a.score > b.score || (a.score == b.score && a.id < b.id)); } int main(){ priority_queue<Song> pq; int n,t,r; cin>>n>>t; for(int i=1;i<=n;++i){ cin >> r; pq.push({i,r}); } while(t--){ auto top_song=pq.top(); cout<<top_song.id<<"\n"; vector<int> remainders; pq.pop(); if(top_song.score%(n-1)!=0){ for(int j=0;j<top_song.score%(n-1);++j) remainders.push_back(j); } while(!pq.empty()){ Song current = pq.top(); pq.pop(); current.score += top_song.score/(n-1); if (!remainders.empty()) { current.score++; remainders.erase(remainders.begin()); } pq.push(current); } // Reinsert the played song with zero points back into queue. pq.push({top_song.id, 0}); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值