2019年湘潭大学程序设计竞赛(A-H)

A - Who`s better?

链接:https://ac.nowcoder.com/acm/contest/893/A
 

题目描述

ICPC比赛中,谁通过的题数多,谁排名靠前;在通过题数相同的情况下,谁的罚时少,谁排名靠前;如果前两者都相同,就看最后正确提交的时间,谁早最排名靠前。 现在给你两个队伍的正确通过的题数、罚时和最后正确提交时间,请判断一下,谁的排名更靠前?

输入描述:

 

只有一组测试样例,两行,每行三个整数n(0≤n≤13),p(1≤p≤1000),s(1≤s≤300)n(0≤n≤13),p(1≤p≤1000),s(1≤s≤300),依次表示两个队的正确通过的题数、罚时和最后正确提交时间。

输出描述:

输出一行(末尾要换行符)。
如果是第1个队排名靠前,输出1;如果是2队,输出2;如果无法分辨,输出"God"。

示例1

输入

复制

1 10 10
1 22 2

输出

复制

1

示例2

输入

复制

1 10 10
2 42 20

输出

复制

2

示例3

输入

复制

1 10 10
1 10 10

输出

复制

God

解题思路

直接比较即可

代码如下

#include <iostream>
using namespace std;
int main()
{
	int n1, p1, s1;
	int n2, p2, s2;
	cin >> n1 >> p1 >> s1;
	cin >> n2 >> p2 >> s2;
	if(n1 > n2){
		cout << 1 << endl;
	}
	else if(n1 < n2){
		cout << 2 << endl;
	}
	else {
		if(p1 < p2)
			cout << 1 << endl;
		else if(p1 > p2)
			cout << 2 << endl;
		else {
			if(s1 < s2)
				cout << 1 << endl;
			else if(s1 > s2)
				cout << 2 << endl;
			else
				cout << "God" << endl;
		}
	}
	return 0;
}

 

B-Number

链接:https://ac.nowcoder.com/acm/contest/893/B
来源:牛客网
 

题目描述

Bonnie得到了一个数字n。
现在她想对这个数字不断的做一种操作:

  • 如果n的最后一位数码是0,那么她就把n除以10;
  • 否则她把这个数加上1;
  • 直到n变为一个不大于1的数。

给定n,请问Bonnie需要做多少次操作?

输入描述:

第一行一个数字T(1≤T≤300000)T(1≤T≤300000)--样例个数。

每个样例仅一行一个数字n(1≤n≤109)n(1≤n≤109)。

输出描述:

每个样例输出一行一个数字—Bonnie需要做的操作次数。

示例1

输入

复制

6  
9  
99  
2  
11032  
1000000000  
62

输出

复制

2
3
9
44
9
13

说明

第一个样例: 9→10→19→10→1
第二个样例: 99→100→10→199→100→10→1

解题思路

模拟即可,见注释

#include <iostream>
using namespace std;
int main()
{
	std::ios::sync_with_stdio(false);
	int n;
	cin >> n;
	while(n --){
		string str;
		cin >> str;
		int ans = 0;
		for(int i = str.size() - 1; i >= 0; i --){
			int x = str[i] - '0';
			if(i == 0 && x == 1){  //只剩1的时候结束
				break;
			}
			if(!x){  //此位是0,直接去掉
				ans ++;
			}
			else if(x == 10){
				ans ++;  //去掉一个0
				if(i != 0)   //进位
					str[i - 1] ++;
			}
			else {
				ans += 10 - x + 1;  //加上那么多的1 and 去掉一个0
				if(i != 0)  //进位
					str[i - 1] ++;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

 C-Math Problem

链接:https://ac.nowcoder.com/acm/contest/893/C
来源:牛客网
 

题目描述

已知整数a,a3a,a3除192的余数是1。求区间[L,R]之间满足条件的a的累加和是多少?

输入描述:

第一行是一个整数T(1≤T≤10000)T(1≤T≤10000),表示样例的个数。
每个样例包含两个整数L,R,1≤L≤R≤109L,R,1≤L≤R≤109。

输出描述:

每行输出一个样例的结果。

示例1

输入

复制

1
1 10

输出

复制

1

解题思路

求一个等差数列,具体见注释

代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 10005
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int main()
{
	int T;
	cin >> T;
	while(T --){
		ll l, r;
		cin >> l >> r;
		ll ra = (r - 1) / 192 + 1; //1-r存在多少满足条件的数
		ll la = (l - 1) / 192 + 1; //1-l存在多少满足条件的数
		//因为l不一定满足条件,不满足时la++
		if((l - 1) % 192)
			la ++; 
		//此时这个区间内的满足条件的序列为(la-1)*192+1 la*192+1 ... (ra-1)*192+1
		if(la > ra){   //不存在满足条件的数 
			cout << 0 << endl;
			continue;
		} 
		//提取1和192,然后等差数列求和
		ll sum = (ra - la + 1) + 192 * (la + ra - 2) * (ra - la + 1) / 2; 
		cout << sum << endl;
	}
	return 0;
}

 

D-Stone

链接:https://ac.nowcoder.com/acm/contest/893/D
来源:牛客网
 

题目描述

有n堆石子排成一排,第i堆石子有aiai个石子。
每次,你可以选择任意相邻的两堆石子进行合并,合并后的石子数量为两堆石子的和,消耗的体力等价于两堆石子中石子数少的那个。
请问,将所有的石子合并成一堆,你所消耗的体力最小是多少?

输入描述:

第一行是一个整数T(1≤T≤20)T(1≤T≤20),表示样例的个数。
每个样例的第一行是一个整数n(1≤n≤10000)n(1≤n≤10000),表示石子堆的数量。
第二行是n个整数ai(1≤ai≤109)ai(1≤ai≤109)

输出描述:

每行输出一个样例的结果。

示例1

输入

复制

2
2
1 2
1
1

输出

复制

1
0

说明

巨大的输入,请使用C风格的输入。

解题思路

贪心,对于n堆石子,要合并成一堆,至少需要合并n-1次,每次合并消耗的体力等价于两堆石子中石子数少的那个, 把所有石子的和加起来减去最大的那堆即可。

代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 10005
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int a[maxn];
int main()
{
	int T;
	cin >> T;
	while(T --){
		int n;
		cin >> n;
		ll ans = 0;
		int maxx = 0;
		for(int i = 1; i <= n; i ++){
			scanf("%d", &a[i]);
			ans += a[i];
			if(a[i] > maxx){
				maxx = a[i];
			}
		}
		cout << ans - maxx << endl;	
	}
	return 0;
}

 

E-Watermelon

链接:https://ac.nowcoder.com/acm/contest/893/E
来源:牛客网
 

题目描述

在ACM队暑假集训的某一天,实验室里有 n n个人。因为天气很热,大家都很想吃西瓜。
于是Eric买了 m m个西瓜拿到了实验室。
Eric把这 n n个人分别编号为 1,2,3...n 1,2,3...n,他希望这 n n个人循环轮流来吃西瓜。
也就是说从 1 1号开始,然后 2 2号, 3 3号...  n n号依次吃西瓜, n n号吃完后又轮到 1 1号来吃,直到这 m m个西瓜被吃完。
而这 n n个人每个人有一个肚量值,第i个人的肚量值为 ai ai。
lililalala是这 n n个人肚量值最大的人,不仅如此,他还非常贪吃,每次轮到他吃西瓜时,都会直接吃掉等同于他的度量值数量的西瓜。如果剩余的西瓜已经不够吃了,那么他会把所有西瓜直接吃完。(是的他很能吃)
除了lililalala以外的其他人,对于第 i i号每次吃西瓜可以选择吃掉 [1,ai] [1,ai]中任意整数数量个西瓜。当然,不能超过当前剩余的西瓜数量。
为了使吃西瓜更有意思一些,Eric规定如果在轮到某个人吃西瓜时没有西瓜了,那么由他来打扫一次实验室。(是的大家都很能吃)
其他人都觉得lililalala吃的太多了应该由他来打扫卫生。请问在其他人串通好的情况下能否合理安排每个人的行动使得吃完西瓜后由lililalala来打扫卫生?

输入描述:

第一行一个数字T(1≤T≤50)T(1≤T≤50)--样例个数。 
每个样例包含两行。 
第一行两个数字n,m(1≤n≤105,0≤m≤106)n,m(1≤n≤105,0≤m≤106)。
第二行 n n个数字a1,a2...an(1≤ai≤107)a1,a2...an(1≤ai≤107),以空格分隔。 
保证所有样例中肚量最大的人只有一个。这个人是lililalala。 
保证所有样例中 ∑n ∑n不超过 3×106 3×106。 
保证所有样例中 ∑m ∑m不超过 107 107。

输出描述:

每个样例输出一行,输出”YES”如果合理安排可以使得最后由lililalala来打扫卫生,否则输出“NO”。

示例1

输入

复制

2  
4 3  
1 2 3 2  
5 8  
1 2 3 2 1

输出

复制

YES
NO

说明

第一个样例中lililalala是3号:
一种可行的方案:
1号吃掉1个西瓜
2号吃掉2个西瓜
第一次到3号吃西瓜时就没有西瓜可以吃了,所以3号需要打扫卫生。

第二个样例无论怎样安排各个人在每一次吃西瓜的数量,也不能达成目标。

解题思路

#include <iostream>
#include <cstdio>
#define maxn 100005
using namespace std;
typedef long long ll;
ll a[maxn];
int main()
{
	int T;
	cin >> T;
	while(T --){
		int n, m;
		scanf("%d%d", &n, &m);
		ll maxx = -1;
		int k = 0;  //最能吃的人的编号
		for(int i = 1; i <= n; i ++){
			scanf("%lld", &a[i]);
			if(a[i] > maxx){
				maxx = a[i];
				k = i;
			}
		}
		ll min_s, max_s; //最大的人前面的最小最大消耗
        ll min_c, max_c;  //一个周期的最大最小消耗
		min_s = max_s = min_c = max_c = 0;
		for(int i = 1; i <= n; i ++){
			if(i < k){
				min_s ++;
				max_s += a[i];
			}
			min_c ++;
			max_c += a[i];
			if(i == k)
				min_c += a[i] - 1;
		}	
		ll max_left = m - min_s;  //轮到k时最多剩下
		ll min_left = m - max_s;  //轮到k时最少剩下
		bool flg = false;
		while(max_left >= 0){	//如果轮到k时最多剩下的小于0了,则说明不可能
			if(min_left <= 0){  //轮到k时剩下的区间饱和了0,则可以
				flg = true;
				break;
			}
			max_left -= min_c;
			min_left -= max_c;
		}
		if(flg || n == 1)  //特判n==1的情况
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
	}
	return 0;
}

F-Black & White

链接:https://ac.nowcoder.com/acm/contest/893/F
来源:牛客网
 

题目描述

你有一个长度为 n 的 01 串S,你可以执行最多 m 次操作。
对于每次操作,你可以选择一个位置 i 满足 1≤i≤n1≤i≤n,翻转这一位的值,0变成1,1变成0。
定义一个 01 串的价值为其中最长连续0的个数和最长连续1的个数的较大值,求S在经过最多m次操作后的最大价值。

输入描述:

* 第一行一个整数 T ,表示接下来有 T 个样例。
* 首先输入n,m,表示S串的长度n和操作次数m,其中1≤n≤1000001≤n≤100000,0≤m≤10000≤m≤1000;
* 接下来输入一个长度为n的字符串S。

输出描述:

一个整数,表示题面上描述的最大价值。

示例1

输入

复制

2
5 1
00101
2 1
01

输出

复制

4
2

说明

第一个串翻转第三个位置,00001的价值为4;第二个串翻转第一个位置,11的价值为2。

解题思路

求一次前缀和,维护1的个数。然后二分答案,若要求的答案为x,对于每一个以i为结尾的长度为x的字符串,比较其max(1的数量,0的数量)+m是否大于x,若大于x,则说明答案x存在。

代码如下

#include <iostream>
#define maxn 100005
using namespace std;
int a[maxn]; //前缀中1的个数
int n, m;
string str;
bool work(int x)
{
	for(int i = x - 1; i < str.size(); i ++){
		int num1 = a[i] - a[i - x + 1] + str[i - x + 1] - '0';
		int maxx = max(num1, x - num1);
		if(maxx + m >= x)
			return true;
	}
	return false;
}
int main()
{
	std::ios::sync_with_stdio(false);
	int T;
	cin >> T;
	while(T --){		
		cin >> n >> m;
		cin >> str;
		a[0] = str[0] - '0';
		for(int i = 1; i < str.size(); i ++){
			a[i] = a[i - 1] + str[i] - '0';
		}
		int l = 1;
		int r = str.size();
		int mid = (l + r + 1) / 2;
		while(l < r){
			if(work(mid))
				l = mid;
			else
				r = mid - 1;
			mid = (l + r + 1) / 2;
		}
		cout << mid << endl;
	}
	return 0;
}

 

G-Truthman or Fakeman

链接:https://ac.nowcoder.com/acm/contest/893/G
来源:牛客网
 

题目描述

有n个人在玩一个身份扮演的游戏。
把这n个人编号为1,2,3...n。
其中每个人会扮演下面两种身份中的一种:
Truthman:当某个人扮演Truthman时,这个人只会说真话。
Fakeman:当某个人扮演Fakeman时,这个人只会说假话。
这n个人是互相知道身份的,但是Casya作为一个旁观者不知道任何一个人的身份。
为了让Casya有可能推断这些人的身份,这n个人说了m句话。
每句话的内容只包含某人对某人身份的一条描述,且被Casya记录为以下形式:
u,v,0 -- u认为v是一个Fakeman;
u,v,1 -- u认为v是一个Truthman;
当然这些话不一定都是真话,这取决于说话的人的身份。
但是可以肯定的是身份只有两种,也就是说某个人不是Truthman就是Fakeman。

Casya想知道不违反上面的条件和记录最少有多少个Fakeman,除此之外他还想得到一组在此情况下的一组合理的解—即所有人的身份。或者确定记录本来就是矛盾的所以没有任何符合条件的解。

输入描述:

第一行一个数字T(1≤T≤30)T(1≤T≤30)--样例个数。 
其中每个样例:
第一行两个数字n,m(1≤n≤105,0≤m≤105)n,m(1≤n≤105,0≤m≤105)。
然后m行,每一行包含三个整数u,v,w(1≤u,v≤n,w={0,1})u,v,w(1≤u,v≤n,w={0,1})
保证所有样例中∑n∑n不超过2×1062×106。 
保证所有样例中∑m∑m不超过2×1062×106。

输出描述:

每个样例输出一行。
如果存在合理的解,输出一个长度为n的字符串,且只包含’0’或’1’。
其中第i个字符为’0’表示$i$是一个Fakeman,为’1’表示i是一个Truthman。并使得Fakeman的数量最少。如果有多个解符合要求,输出任意一个即可。

如果不存在任何合理的解,输出-1。

示例1

输入

复制

2  
5 4  
1 2 1  
2 3 0  
2 4 0  
1 5 0  
3 3  
1 2 1  
1 3 0  
2 3 1

输出

复制

00111
-1

解题思路

并查集。如果a说b是1,那么如果a是1,b也是1,如果a是0,b也是0,即a和b的身份是相同的。反之,如果a说b是0,那么a和b的身份是不同的。由此,我们进行并查集操作,以及要遵循,和我身份不同的人身份不同的,和我身份相同。

代码如下

#include <iostream>
#include <map>
#include <cstdio> 
#include <vector>
#include <cstring>
#define maxn 200005
using namespace std;
int par[maxn];
bool vis[maxn];
int f(int p)
{
	return p == par[p] ? p : par[p] = f(par[p]);
}
vector<int> vec[maxn];
int main()
{
	int T;
	cin >> T;
	while(T --){
		int n, m;
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= 2*n; i ++){
			par[i] = i;
		}
		for(int i = 1; i <= m; i ++){
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			if(w){
				par[f(u)] = f(v);
				par[f(u + n)] = f(v + n);
			}
			else {
				par[f(v)] = f(u + n);
				par[f(u)] = f(v + n);
			}
		}
		bool flg = true;
		for(int i = 1; i <= n; i ++){
			if(f(i) == f(i + n)){
				flg = false;
				break;
			}
		}
		if(!flg)
			cout << -1 << endl;
		else {
			for(int i = 1; i <= n; i ++){
				vec[f(i)].push_back(i);
			}
			map<int, int> mp;
			memset(vis, 0, sizeof(vis));
			for(int i = 1; i <= 2 * n; i ++){
				if(!vis[i] && vec[i].size()){
					vis[i] = true;
					int r = f(vec[i][0] + n);
					vis[r] = true;
					if(vec[i].size() > vec[r].size()){
						for(int j = 0; j < vec[i].size(); j ++){
							mp[vec[i][j]] = 1;
						}
						for(int j = 0; j < vec[r].size(); j ++)
							mp[vec[r][j]] = 0;
					}
					else {
						for(int j = 0; j < vec[i].size(); j ++){
							mp[vec[i][j]] = 0;
						}
						for(int j = 0; j < vec[r].size(); j ++)
							mp[vec[r][j]] = 1;
					}
				}
			}
			for(int i = 1; i <= n; i ++){
				printf("%d", mp[i]);
			}
			putchar('\n');
		}
		for(int i = 1; i < maxn; i ++)
			vec[i].clear();
	}
	return 0;
}

H-Chat

链接:https://ac.nowcoder.com/acm/contest/893/H
来源:牛客网
 

题目描述

在Casya生活的世界里,一天由m个小时组成。
最近Casya的女神终于答应在接下来的n天中与Casya聊天,Casya非常激动。

在这n天中的每一天的每一个小时中女神可能会在线或者不在线,
某个小时如果女神如果在线且Casya在线的话他们就会开心的聊一个小时;
反之如果女神在线Casya没有在线的话女神就会认为Casya放了她的鸽子而积累一点生气度。

而Casya是个很懒惰的人,他每天只愿意上线一次,当他某天下线后就不愿意再上线了。
换句话说,他每天在线的时间必须是连续的。

现在Casya已经知道每一天的每个小时女神是否会在线

Casya希望在这n天中女神的总生气度不超过k,在此前提下Casya希望他的总上线时间最小。
假设每个小时Casya和女神要么完整在线要么完整不在线,请问Casya在这n天中最小的总上线时间是多少小时?

输入描述:

第一行一个数字T(1≤T≤30)T(1≤T≤30)--样例个数。

每个样例第一行三个数字n,m,k(1≤n,m≤200,0≤k≤200)n,m,k(1≤n,m≤200,0≤k≤200)。
然后这n行,每行一个长度为m的只含'0'和'1'的字符串,
第i个字符串的第j个字符为'0'代表女神第i天的第j个小时不在线,为'1'表示女神第i天的第j个小时在线。

保证所有样例中∑n×m∑n×m不超过5×1055×105。

输出描述:

每个样例输出一行一个数字--Casya在这n天中最小的总上线时间

示例1

输入

复制

2  
2 5 1  
01001  
10110  
2 5 0  
01001  
10110  

输出

复制

5
8

说明

第一个样例:
一种可行的方案:
Casya第一天只在第2个小时上线,第五个小时女生在线而Casya不在线,生气度积累1;
Casya第一天在第1、2、3、4个小时上线。
Casya的总上线时间是1+4=5。
第二个样例:
只能老老实实上线。
Casya第一天在第2、3、4、5个小时上线;
Casya第一天在第1、2、3、4个小时上线。
Casya的总上线时间是4+4=8。

解题思路

可以转为为分组背包问题,以生气值为背包容量。

#include <iostream>
#include <vector>
#include <cstring>
#define maxn 205
using namespace std;
int dp[maxn][maxn]; 
int a[maxn][maxn]; //第i天造成j点生气值最多不在线时间
string str[maxn];
int c[maxn];
int main()
{
	std::ios::sync_with_stdio(false);
	int T;
	cin >> T;
	while(T --){
		int n, m, k;
		cin >> n >> m >> k;
		for(int i = 1; i <= n; i ++){
			cin >> str[i];
		}
		memset(dp, 0, sizeof(dp));
		memset(a, 0, sizeof(a));
		for(int i = 1; i <= n; i ++){
			int s, t;
			s = t = m;
			int qz[maxn], hz[maxn];
			for(int j = 0; j < m; j ++){
				if(str[i][j] == '1'){
					s = j;
					break;
				}
			}
			for(int j = m - 1; j >= 0; j --){
				if(str[i][j] == '1'){
					t = m - j - 1;
					break;
				}
			}
			int cnt = -1;
			for(int j = 0; j < m; j ++){
				if(str[i][j] == '1'){
					qz[++cnt] = j;
				}
			}	
			cnt = -1;
			for(int j = m - 1; j >= 0; j --){
				if(str[i][j] == '1'){
					++ cnt;
					hz[cnt] = m - j - 1;
					a[i][cnt] = max(qz[cnt], hz[cnt]);
				}
			}
			a[i][0] = min(m, s + t);
			cnt ++;
			a[i][cnt] = m;
			c[i] = cnt;
			for(int j = 1; j < cnt; j ++){  
				for(int u = 0; u <= j; u ++)
					a[i][j] = max(qz[u] + hz[j - u], a[i][j]);  //前缀和后缀
			}
		}
		for(int i = 1; i <= n; i ++){
			for(int j = 0; j <= k; j ++){
				for(int u = 0; u <= min(j, c[i]); u ++)
					dp[i][j] = max(dp[i][j], dp[i - 1][j - u] + a[i][u]);
			}
		}
		int ans = 0;
		for(int i = 0; i <= k; i ++){
			if(dp[n][i] > ans)
				ans = dp[n][i];
		}
		cout << n * m - ans << endl;
	}
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值