USACO 2021 January Contest, Bronze

USACO 2021 January Contest, Bronze

Problem 1. Uddered but not Herd

一个鲜为人知的事实是,奶牛拥有自己的文字:「牛文」。牛文由 26 个字母 ‘a’ 到 ‘z’ 组成,但是当奶牛说牛文时,可能与我们所熟悉的 ‘abcdefghijklmnopqrstuvwxyz’ 不同,她会按某种特定的顺序排列字母。

为了打发时间,奶牛 Bessie 在反复哼唱牛文字母歌,而 Farmer John 好奇她唱了多少遍。

给定一个小写字母组成的字符串,为 Farmer John 听到 Bessie 唱的字母,计算 Bessie 至少唱了几遍完整的牛文字母歌,使得 Farmer John 能够听到给定的字符串。Farmer John 并不始终注意 Bessie 所唱的内容,所以他可能会漏听 Bessie 唱过的一些字母。给定的字符串仅包含他记得他所听到的字母。

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

输入的第一行包含 26 个小写字母 ‘a’ 到 ‘z’ 的牛文字母表顺序。下一行包含一个小写字母组成的字符串,为 Farmer John 听到 Bessie 唱的字母。字符串的长度不小于 1 1 1 且不大于 1000 1000 1000

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

输出 Bessie 所唱的完整的牛文字母歌的最小次数。

输入样例:
abcdefghijklmnopqrstuvwxyz
mood
输出样例:
3

在这个样例中,牛文字母表与日常的字母表的排列一致。

Bessie 至少唱了三遍牛文字母歌。有可能 Bessie 只唱了三遍牛文字母歌,而 Farmer John 听到了以下被标记为大写的字母。

abcdefghijklMnOpqrstuvwxyz
abcdefghijklmnOpqrstuvwxyz
abcDefghijklmnopqrstuvwxyz
测试点性质:
  • 测试点 2-5 中,牛文字母表与日常的字母表相同。
  • 测试点 6-10 没有额外限制。

供题:Nick Wu

分析:

略。

#include<bits/stdc++.h>
using namespace std;
string s,st;
int main(){
	freopen("herd.in","r",stdin);
	freopen("herd.out","w",stdout);
	cin>>s>>st;
	int pos=0,ans=1;//当成环查找
	for(int i=0;i<st.length();++i){
		if(pos==26)//26放在循环开始判断,结尾正好找到26跳出时不需对ans操作
			pos=0,++ans;
		while(s[pos++]!=st[i])
			if(pos==26)
				pos=0,++ans;
	}cout<<ans<<endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

Problem 2. Even More Odd Photos

Farmer John 正再一次尝试给他的 NN 头奶牛拍照( 2 ≤ N ≤ 1000 2≤N≤1000 2N1000)。

每头奶牛有一个范围在 1 … 100 1…100 1100 之内的整数的「品种编号」。Farmer John 对他的照片有一个十分古怪的构思:他希望将所有的奶牛分为不相交的若干组(换句话说,将每头奶牛分到恰好一组中)并将这些组排成一行,使得第一组的奶牛的品种编号之和为偶数,第二组的编号之和为奇数,以此类推,奇偶交替。

Farmer John 可以分成的最大组数是多少?

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

输入的第一行包含 NN。下一行包含 NN 个空格分隔的整数,为 NN 头奶牛的品种编号。

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

输出 Farmer John 的照片中的最大组数。可以证明,至少存在一种符合要求的分组方案。

输入样例:
7
1 3 5 7 9 11 13
输出样例:
3

在这个样例中,以下是一种分成最大组数三组的方案。将 1 和 3 分在第一组,5、7 和 9 分在第二组,11 和 13 分在第三组。

输入样例:
7
11 2 17 13 1 15 3
输出样例:
5

在这个样例中,以下是一种分成最大组数五组的方案。将 2 分在第一组,11 分在第二组,13 和 1 分在第三组,15 分在第四组,17 和 3 分在第五组。

供题:Nick Wu

分析:

很显然,这题是个奇偶性判断,计算出奇数偶数的个数 n u m 1 , n u m 2 num1,num2 num1,num2 ,然后:

①偶数先手,则 n u m 1 = = n u m 2 num1==num2 num1==num2 (此时奇数结尾),或者 n u m 1 + 1 = = n u m 2 num1+1==num2 num1+1==num2 (此时偶数结尾)。
②两个奇数可以构成一个偶数,偶数构不成奇数。

则,如果 n u m 1 + 1 < n u m 2 num1+1<num2 num1+1<num2 ,则必定有多个偶数挤在一起。
如果 n u m 1 > n u m 2 num1>num2 num1>num2 ,则必定有多个两个奇数构成偶数;当两个奇数构成一个偶数时, n u m 1 − = 2 ,   + + n u m 2 num1-=2,~++num2 num1=2, ++num2 ,因为最终 n u m 2 = = n u m 1 ∣ ∣ n u m 2 = = n u m 1 + 1 num2==num1||num2==num1+1 num2==num1∣∣num2==num1+1 ,则讨论 ( n u m 1 − n u m 2 ) m o d    3 (num1-num2)\mod3 (num1num2)mod3 的所有可能情况(即完全剩余系)。

如:(定义 d f c = n u m 1 − n u m 2 dfc=num1-num2 dfc=num1num2 ,所出现的所有 / 符号表示整除)
n u m 2 = 0 ,   n u m 1 = 6 num2=0,~num1=6 num2=0, num1=6 ,贪心可得 ( n u m 2 = 1 ,   n u m 1 = 4 ) → ( n u m 2 = 2 ,   n u m 1 = 2 ) (num2=1,~num1=4)\to(num2=2,~num1=2) (num2=1, num1=4)(num2=2, num1=2) ,则 a n s = 2 ( n u m 2 + d f c / 3 ) ans=2(num2+dfc/3) ans=2(num2+dfc/3)
n u m 2 = 0 ,   n u m 1 = 7 num2=0,~num1=7 num2=0, num1=7 ,同理可得 ( n u m 2 = 1 ,   n u m 1 = 5 ) → ( n u m 2 = 2 ,   n u m 1 = 3 ) → ( n u m 2 = 2 ,   n u m 1 = 1 ) (num2=1,~num1=5)\to(num2=2,~num1=3)\to(num2=2,~num1=1) (num2=1, num1=5)(num2=2, num1=3)(num2=2, num1=1) ,则 a n s = 2 ( n u m 2 + d f c / 3 ) − 1 ans=2(num2+dfc/3)-1 ans=2(num2+dfc/3)1
n u m 2 = 0 ,   n u m 1 = 8 num2=0,~num1=8 num2=0, num1=8 ,同理可得 ( n u m 2 = 1 ,   n u m 1 = 6 ) → ( n u m 2 = 2 ,   n u m 1 = 4 ) → ( n u m 2 = 3 ,   n u m 1 = 2 ) (num2=1,~num1=6)\to(num2=2,~num1=4)\to(num2=3,~num1=2) (num2=1, num1=6)(num2=2, num1=4)(num2=3, num1=2) ,则 a n s = 2 ( n u m 2 + d f c / 3 ) + 1 ans=2(num2+dfc/3)+1 ans=2(num2+dfc/3)+1

则有如下代码:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1010
int n,oe[2];
int main(){
	freopen("group.in","r",stdin);
	freopen("group.out","w",stdout);
	cin>>n;
	int id;
	for(int i=1;i<=n;++i){
		cin>>id;
		++oe[id%2];
	}int dfc=oe[1]-oe[0];//difference
	if(dfc<0)//偶比奇大
		cout<<oe[1]*2+1<<endl;
	else if(dfc%3==0)//整除
		cout<<(oe[0]+dfc/3)*2<<endl;
	else if(dfc%3==1)//余1
		cout<<(oe[0]+dfc/3)*2-1<<endl;
	else if(dfc%3==2)//余2
		cout<<(oe[0]+dfc/3)*2+1<<endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

Problem 3. Just Stalling

Farmer John 有 N N N 头奶牛( 1 ≤ N ≤ 20 1≤N≤20 1N20),高度为 a 1 … a N a_1…a_N a1aN。他的牛栏有 N N N 个牛棚,高度限制分别为 b 1 … b N b_1…b_N b1bN(例如,如果 b 5 = 17 b_5=17 b5=17,那么一头高度不超过 17 17 17 的奶牛可以住在牛棚 5 5 5 里)。Farmer John 有多少种不同的方式安排他的奶牛,使得每头奶牛均住在不同的牛棚里,并且使得每个牛棚的高度限制均得到满足?

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

输入的第一行包含 N N N。第二行包含 N N N 个空格分隔的整数 a 1 , a 2 , … , a N a_1,a_2,…,a_N a1,a2,,aN。第三行包含 NN 个空格分隔的整数 b 1 , b 2 , … , b N b_1,b_2,…,b_N b1,b2,,bN。所有的高度和高度限制均在范围 [ 1 , 109 ] [1,109] [1,109] 内。

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

输出 Farmer John 可以将每头奶牛安排到不同的牛棚里,使得每个牛棚的高度限制均得到满足的方法数。注意输出的数量可能需要使用 64 位整数型,例如 C++ 中的 long long。

输入样例:
4
1 2 3 4
2 4 3 4
输出样例:
8

在这个例子中,我们不能将第三头奶牛安排到第一个牛棚里,因为 3 = a 3 > b 1 = 2 3=a_3>b_1=2 3=a3>b1=2。类似地,我们不能将第四头奶牛安排到第一或第三个牛棚里。一种符合高度限制的安排方式为将奶牛 1 1 1 安排到牛棚 1 1 1,奶牛 2 2 2 安排到牛棚 2 2 2,奶牛 3 3 3 安排到牛棚 3 3 3,奶牛 4 4 4 安排到牛棚 4 4 4

测试点性质:
  • 测试点 1-5 满足 N ≤ 8 N≤8 N8
  • 测试点 6-12 没有额外限制。

供题:Shreyas Thumathy

分析:

状压dp:

如果能想到状压,在纸上模拟一遍就能看出来组合数学。

乘法原理:

计算出每一头奶牛能住进的棚子数量,从个子高的奶牛开始选(因为可选的少),由于个子矮的奶牛一定能住进个子高奶牛能住进的棚子,则在高奶牛选择了一个棚子后,所有矮奶牛的可选棚子数都减少一个,即 a n s = Π i = 1 n ( v h [ i ] − n + i ) ans=\Pi_{i=1}^{n}(vh[i]-n+i) ans=Πi=1n(vh[i]n+i) ,则有以下两种代码。

O ( n 2 ) O(n^2) O(n2)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,a[25],b,vh[25];
ll ans=1;
int main(){
	freopen("stalling.in","r",stdin);
	freopen("stalling.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	sort(a+1,a+1+n);
	for(int i=1;i<=n;++i){
		cin>>b;//不需要记录b
		for(int j=1;j<=n;++j){
			if(a[j]>b)
				break;
			++vh[j];//每读入一个牛棚高度都对每头奶牛能住进的棚子数量进行操作
		}
	}for(int i=n;i>=1;--i)
		ans*=(vh[i]-n+i);
	cout<<ans<<endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}
O ( n l o g n ) O(nlogn) O(nlogn)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,a[25],b[25],vh[25];
ll ans=1;
int main(){
	freopen("stalling.in","r",stdin);
	freopen("stalling.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	sort(a+1,a+1+n);
	for(int i=1;i<=n;++i)
		cin>>b[i];
	sort(b+1,b+1+n);
	int pos=n;
	vh[n+1]=0;
	for(int i=n;i>=1;--i){
		vh[i]=vh[i+1];
		for(int j=pos;j>=1;--j){//没必要每次都从头查找
			if(b[j]<a[i])
				break;
			pos=j-1;
			++vh[i];
		}
	}for(int i=n;i>=1;--i)
		ans*=(vh[i]-n+i);
	cout<<ans<<endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值