USACO 2022 December Contest

参加的USACO的第一次比赛。没有打现场赛,后来跟着看了看题目,感觉总的来说,难度中等偏上,虽有些乏力,但是没有超出能力范围。下次争取打现场赛。

Problem 1. Cow College

Farmer John 计划为奶牛们新开办一所大学!

有 N(1≤N≤105)头奶牛可能会入学。每头奶牛最多愿意支付 ci 的学费(1≤ci≤106)。 Farmer John 可以设定所有奶牛入学需要支付的学费。如果这笔学费大于一头奶牛愿意支付的最高金额,那么这头奶牛就不会入学。Farmer John 想赚尽可能多的钱,从而可以给他的讲师提供一笔可观的工资。请求出他能赚到的钱的数量,以及此时应当收取多少学费。

输入格式(从终端 / 标准输入读入):

输入的第一行包含 N。第二行包含 N 个整数 c1,c2,…,cN,其中 ci 是奶牛 i 愿意支付的最高学费金额。

输出格式(输出至终端 / 标准输出):

输出 Farmer John 可以赚到的最大金额以及最优情况下他应该收取的学费。如果有多个解,输出收取学费最小的解。

注意这个问题涉及到的整数可能需要使用 64 位整数型(例如,Java 中的 "long",C/C++ 中的 "long long")。

输入样例:

4

1 6 4 6

输出样例:

12 4

如果 Farmer John 收费 4,那么 3 头奶牛将会入学,从而使他赚取 3⋅4=12 的金额。

测试点性质:

测试点 2-4 满足 ci≤1,000。

测试点 5-8 满足 N≤5,000。

测试点 9-12 没有额外限制。

题解

算法思想:排序。

要取得最大值,只需要取出某个值,然后对这个值可以收取的学费进行比较,取出最大值即可。

``` cpp

#include <bits/stdc++.h>

#define N 100005

using namespace std;

int n, ans, a[N];

long long now, mx;

int main()

{

cin >> n;

for (int i = 1; i <= n; i++)

cin >> a[i];

sort(a + 1, a + n + 1);

for (int i = 1; i <= n; i++)

{

now = (long long)a[i] * (n - i + 1);

if (now > mx)

mx = now, ans = a[i];

}

cout << mx << " " << ans << endl;

return 0;

}

```

Problem 2. Feeding the Cows

Farmer John 有 N(1≤N≤105)头奶牛,每头奶牛的品种是更赛牛(Guernsey)或荷斯坦牛(Holstein)之一。她们沿水平方向排成一行,奶牛们占据的位置编号为 1…N。

由于奶牛们都饿了,FJ 决定在 1…N 中的某些位置上种植草地。更赛牛和荷斯坦牛喜欢不同类型的草,所以如果 Farmer John 决定在某个位置种草,他必须选择种植更赛牛喜欢的草或荷斯坦牛喜欢的草——他不能在同一个位置同时种两种草。种植的每一片草地都可以喂饱数量不限的相应品种的奶牛。

每头奶牛愿意移动至多 K(0≤K≤N−1)个位置以前往一个草地。求出喂饱所有奶牛所需种植的最小草地数量。此外,输出一种使用最小草地数量喂饱所有奶牛的种植方案。任何满足上述条件的方案均视为正确。

输入格式(从终端 / 标准输入读入):

每个测试用例包含 T 个子测试用例,为一种奶牛的排列。输入的第一行包含 T(1≤T≤10)。以下是 T 个子测试用例。

每个子测试用例的第一行包含 N 和 K。第二行包含一个长度为 N 的字符串,其中第 i 个字符表示第 i 头奶牛的品种(G 表示更赛牛,H 表示荷斯坦牛)。

输出格式(输出至终端 / 标准输出):

对于 T 个子测试用例中的每一个,输出两行。第一行输出喂饱所有奶牛所需的最小草地数量。第二行输出一个长度为 N 的字符串,表示一种使用最小草地数量喂饱所有奶牛的种植方案。第 i 个字符表示第 i 个位置,若不种草则为 '.',若种植喂饱更赛牛的草则为 'G',若种植喂饱荷斯坦牛的草则为 'H'。任何合法的方案均可通过。

输入样例:

6

5 0

GHHGG

5 1

GHHGG

5 2

GHHGG

5 3

GHHGG

5 4

GHHGG

2 1

GH

输出样例:

5

GHHGG

3

.GH.G

2

..GH.

2

...GH

2

...HG

2

HG

注意对于某些子测试用例,存在多种可通过的方案使用最小数量的草地。例如,在第四个子测试用例中,以下是另一个可以通过的答案:

.GH..

这个方案在第二个位置种植一块喂饱更赛牛的草地以及在第三个位置种植一块喂饱荷斯坦牛的草地。这使用了最小数量的草地并确保了所有奶牛都在她们喜欢的草地的 3 个位置以内。

测试点性质:

测试点 2-4 满足 N≤10。

测试点 5-8 满足 N≤40。

测试点 9-12 满足 N≤105。

题解

看题一开始可能会没有什么思路。

分析题目可知,给出的解决方案需要满足的条件有:

1. 喂饱所有牛;

2. 草地数量尽可能少。

从2出发,我们可以使得每一个草都种在里这头牛最远(K)处,就可以使得所需要的草最少。

然后,再考虑,满足喂饱所有牛。

然后可能会出界。(1-N)

这时其实已经取得了最优解,只需要把出界的草移动会界内即可。

```cpp

#include <bits/stdc++.h>

using namespace std;

int main()

{

int t;

cin >> t;

while (t--)

{

int n, k, cnt = 0, h = -1, g = -1;

char s[100005];

int grass[100005];

bool visited[100005];

memset(visited, false, sizeof(visited));

memset(grass, 0, sizeof(grass));

cin >> n >> k >> s;

for (int i = 0; i < n; i++)

{

if (s[i] == 'H')

if (abs(i - h) > k || h == -1)

for (int j = min(i + k, n - 1); j >= max(i - k, 0); j--)

if (!visited[j])

{

grass[j] = 1, visited[j] = true, h = j, cnt++;

break;

}

if (s[i] == 'G')

if (abs(i - g) > k || g == -1)

for (int j = min(i + k, n - 1); j >= max(i - k, 0); j--)

if (!visited[j])

{

grass[j] = 2, visited[j] = true, g = j, cnt++;

break;

}

}

cout << cnt << endl;

for (int i = 0; i < n; i++)

{

if (grass[i] == 0)

cout << '.';

if (grass[i] == 1)

cout << 'H';

if (grass[i] == 2)

cout << 'G';

}

cout << endl;

}

return 0;

}

```

Problem 3. Reverse Engineering

Elsie 有一个程序,接受一个 N(1≤N≤100)个变量的数组 b[0],…,b[N−1] 作为输入,其中每个变量等于 0 或 1,并且返回对输入数组应用一系列 if / else if / else 语句的结果。每个语句检查至多一个输入变量的值,并返回 0 或 1。这类程序的一个例子是:

if (b[1] == 1) return 1;

else if (b[0] == 0) return 0;

else return 1;

例如,如果上方程序的输入是 "10"(即 b[0]=1 及 b[1]=0),那么输出应当为 1。

Elsie 告诉了 Bessie 对于 M(1≤M≤100)个不同输入的正确输出。Bessie 现在正试图对 Elsie 的程序进行逆向工程。不幸的是,Elsie 可能说了谎;可能不存在上述形式的程序行为与 Elsie 所说的均一致。

对于 T(1≤T≤10)个子测试用例中的每一个,判断 Elsie 是否一定在说谎。

输入格式(从终端 / 标准输入读入):

输入的第一行包含 T,为子测试用例的数量。

每一个子测试用例的第一行包含两个整数 N 和 M,以下 M 行,每行包含一个由 N 个 0 或 1 组成的字符串,表示一个输入(即 b[0]…b[N−1] 的值),以及另一个字符(0 或 1)表示输出。相邻的子测试用例之间用空行分隔。

输出格式(输出至终端 / 标准输出):

对于每一个子测试用例,输出一行,包含 "OK" 或 "LIE",分别表示 Elsie 可能没有说谎或是一定在说谎。

输入样例:

4

1 3

0 0

0 0

1 1

2 4

00 0

01 1

10 1

11 1

1 2

0 1

0 0

2 4

00 0

01 1

10 1

11 0

输出样例:

OK

OK

LIE

LIE

以下是第一个子测试用例的一个合法的程序:

if (b[0] == 0) return 0;

else return 1;

以下是第一个子测试用例的另一个合法的程序:

if (b[0] == 1) return 1;

else return 0;

以下是第二个子测试用例的一个合法的程序:

if (b[1] == 1) return 1;

else if (b[0] == 0) return 0;

else return 1;

显然,对于第三个子测试用例不存在对应的合法的程序,因为 Elsie 的程序一定始终对相同的输入产生相同的输出。

可以证明对于最后一个子测试用例不存在对应的合法的程序。

测试点性质:

测试点 2-3 满足 N=2。

测试点 4-5 满足 M=2。

测试点 6-12 没有额外限制。

题解

我们可以尝试构造一个满足条件的程序。

如第一个样例:

0 0 -> 0

0 1 -> 1

1 0 -> 1

1 1 -> 1

竖着看,发现第一位为1时答案都是1.

说明,处理这位的语句是判断语句。

就可以把它去掉。(因为已经return了)

得到

0 0 -> 0

0 1 -> 1

然后,(第二行)0 得到 0 , 1 得到 1。说明,这个程序存在。

按以上思路模拟即可。

```cpp

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int MAXN = 105;

int n , m;

int p[MAXN] , f[MAXN];

string s[MAXN];

bool t[MAXN][MAXN];

signed main()

{

int T;

cin >> T;

while(T--)

{

cin >> m >> n;

bool flag[MAXN] , k[MAXN];

bool l = 1;

for(int i = 1 ; i <= n ; i ++)

{

cin >> s[i] >> p[i];

flag[i] = 0;

}

for(int i = 1 ; i <= m ; i ++)

for(int j = 0 ; j < m ; j ++)

{

int c[2] ={0,0},cc[2]={0,0};

for(int z = 1 ; z <= n ; z ++)

{

if(s[z][j] == '1')

t[z][j] = 1;

else

t[z][j] = 0;

if(flag[z])

continue;

if(cc[t[z][j]] == 2)

cc[t[z][j]] = p[i];

else if(cc[t[z][j]] != p[i])

c[t[z][j]] = 1;

}

for(int z = 1 ; z <= n ; z ++)

if(!c[t[z][j]])

flag[z] = 1;

}

for(int i = 1 ; i <= n ; i ++)

if(!flag[i])

l = 0;

if(l)

cout << "OK" << endl;

else

cout << "LIE" << endl;

}

return 0;

}

```

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值