2019年湘潭大学程序设计竞赛(重现赛) 题解

A题链接: https://ac.nowcoder.com/acm/contest/893/A
水题直接上代码:

#include<bits/stdc++.h>
using namespace std;
int n1, p1, s1, n2, p2, s2;
bool judge() {
	if (n1 == n2 && p1 == p2)return s1 < s2;
	if (n1 == n2)return p1 < p2;
	return n1 > n2;
}
int main() {
	scanf("%d%d%d", &n1, &p1, &s1);
	scanf("%d%d%d", &n2, &p2, &s2);
	if (judge())printf("1");
	else if (n1 != n2 || p1 != p2 || s1 != s2)printf("2");
	else printf("God");
	printf("\n");
	return 0;
}

B题链接: https://ac.nowcoder.com/acm/contest/893/B
解法:我是从最后一位不为0的数开始计数,第一个数num需要的操作数是10-num,再加上除以10这个操作,就是11-num,而之后的数有些许不同,因为前一位已经进了一位,所以总操作数是10-num。其中还要特判一下,例如10000000,这种操作只需要把最后的0去掉就好了,也就是说除掉后面的0,只有一位数,且这位数是1,那就不用操作。
代码:

#include<bits/stdc++.h>
using namespace std;
int read() {
	int x = 0;
	char c = getchar();
	while (c > '9' || c < '0') c = getchar();
	while (c >= '0'&&c <= '9') x = x * 10 + c - '0', c = getchar();
	return x;
}
const int MAX = 1e5 + 10;
int T, N;
int main() {
	T = read();
	while (T--) {
		N = read();
		int ans = 0, flag = 0;
		queue<int> q;
		while (N) {
			if (!flag && N % 10 == 0)ans++;
			else q.push(N % 10), flag = 1;
			N /= 10;
		}
		if (q.size() == 1 && q.front() == 1);
		else ans += 11 - q.front(); 
		q.pop();
		while (!q.empty()) {
			ans += 10 - q.front(); q.pop();
		}
		printf("%d\n", ans);
	}
	return 0;
}

C题链接: https://ac.nowcoder.com/acm/contest/893/C
题解: 直接扫一遍区间肯定会超时。这里需要知道: a 3 a^3 a3 % 192 = (a % 192) * (a % 192) * (a % 192) % 192,所以这里情况无非就是b * b * b % 192 = 1(其中b = a % 192)。通过打表,在b∈[1, 191]之间,只有1满足,所以就只用找到区间[L,R]之间有没有数num,使得num % 192 == 1, 找到之后就可以看做等差数列求和就是了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T, l, r;
int main() {
	scanf("%d", &T);
	while (T--) {
		ll ans = 0;
		scanf("%d%d", &l, &r);
		int p = 0;
		for (int i = l; i <= r; i++)
			if (i % 192 == 1) {
				p = i;
				break;
			}
		if (p == 0)printf("0\n");
		else {
			//公差为192, 首项为p, 项数为num
			ll num = r / 192 - p / 192 + 1;
			if (r % 192 == 0)num--;
			printf("%lld\n", num * p + num * (num - 1) / 2 * 192);
		}
	}
	return 0;
}

D题链接: https://ac.nowcoder.com/acm/contest/893/D
题解: 也算是水题,我一开始还没反应过来,考虑两堆合并,如果其中那个小的数c曾经是由两个数合并,即c = a + b (其中a < b), 那么答案相当于加上了2 * a + b, 但是如果你是一个一个合并,那答案就是加上a + b, 所以肯定是一个一个合并上去,所以只要去掉最大的那堆就好了。
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T, N;
ll x;
int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &N);
		ll ans = 0;
		ll maxx = 0;
		for (int i = 1; i <= N; i++) {
			scanf("%lld", &x);
			ans += x;
			maxx = max(maxx, x);
		}
		printf("%lld\n", ans - maxx);
	}
	return 0;
}

E题链接: https://ac.nowcoder.com/acm/contest/893/E
题解: 这题是后来补的,发现标答的做法非常牛逼(反正我想不出来)。标答是维护一个区间 [ L , R ] [L, R] [L,R],其中 R R R表示的是轮到这个人为止剩余最多的西瓜数(每个人都吃最少的),而 L L L表示剩余的最小西瓜数(每次都吃最多的),因此可以知道轮到当前这个人的时候还剩下 x x x个西瓜, x ∈ [ L , R ] x∈[L, R] x[L,R]。那么在轮到lililalala的时候如果剩余的最大西瓜小于等于0,即 R &lt; = 0 R&lt;=0 R<=0,那么lililalala就铁定没有西瓜吃了,所以这种情况就是题意中的YES;在没轮到lililalala的时候,如果L<=0并且R<=0,也就是说没瓜可吃的情况下(这种情况一定是建立在上一次轮到lililalala有瓜吃的情况),那就是题意中的NO。
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mp(x, y) make_pair(x, y)
#define fi first
#define se second
#define vi vector<int>
#define qi queue<int>
#define si stack<int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 1e5 + 10;
int T, N, M, a[MAX];
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &N, &M);//n人,m个西瓜
        int pos, maxx = 0, L = M, R = M, flag = -1;
        for (int i = 1; i <= N; i++) {
            scanf("%d", &a[i]);
            if (a[i] > maxx)maxx = a[i], pos = i;
        }
        while (flag == -1) {
            for (int i = 1; i <= N; i++) {//维护[L,R]
                if (i == pos && R <= 0) {//轮到lililalala没瓜吃
                    flag = 1;
                    break;
                }
                if (i != pos && L <= 0 && R <= 0) {//没轮到lililalala就已经没瓜吃了
                    flag = 0;
                    break;
                }
                L -= (i == pos ? maxx : 1);
                R -= a[i];
            }
        }
        if (flag)printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

F题链接: https://ac.nowcoder.com/acm/contest/893/F
题意: 给一串只有01的字符串,一次操作能将0变成1,也能将1变成0,问在最多M次操作的情况下,得到的最大连续串(一串都是0或者一串都是1)。
题解: 解法是二分法,分的是最大连续串的长度,这里需要记录的就是一个前缀和,pre[i]记录的是i点(包括i点)之前1的个数,pre2[i]记录的是0的个数,所以二分的时候中间检查能否有pre[i + mid - 1] - pre[i - 1] <= M,也就是区间内1的个数小于M,或者0的个数小于M,如果有那就说明这个区间长度是可以找到的,就这样继续二分下去就是答案。
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 10;
int pre[MAX], pre2[MAX], T, N, M;
char s[MAX];
int main() {
	scanf("%d", &T);
	while (T--) {
		int ans = 0;
		scanf("%d%d", &N, &M);
		scanf("%s", s + 1);
		for (int i = 1; i <= N; i++) {
			int num = (int)(s[i] - '0');
			pre[i] = pre[i - 1], pre2[i] = pre2[i - 1];
			if (num == 0)pre[i]++;
			else pre2[i]++;
		}
		int l = 1, r = N, mid;//二分区间长度
		while (l <= r) {
			mid = l + r >> 1;
			int flag = 0;
			for (int i = 1; i + mid - 1 <= N; i++)
				if (pre[i + mid - 1] - pre[i - 1] <= M) {
					flag = 1;
					break;
				}
			if (flag == 0) {
				for (int i = 1; i + mid - 1 <= N; i++)
					if (pre2[i + mid - 1] - pre2[i - 1] <= M) {
						flag = 1;
						break;
					}
			}
			if (flag == 0)r = mid - 1;//说明长度大了
			else l = mid + 1, ans = max(ans, mid);
		}
		printf("%d\n", ans);
	}
	return 0;
}

H题链接: https://ac.nowcoder.com/acm/contest/893/H
题解: 这题也是补的。标答的意思是分组背包,先求出当天产生i点怒气的最小上线时间,然后以怒气值为背包空间,当天的不同怒气产生的耗时t[i]作为物品。
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mp(x, y) make_pair(x, y)
#define fi first
#define se second
#define vi vector<int>
#define qi queue<int>
#define si stack<int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 210;
int T, N, M, K;
char s[MAX];
int t[MAX], pre[MAX];//t[i]为当前这天产生i点怒气的最小耗时
int DP[MAX][MAX];//DP[i][j]为第i天产生j点怒气的最小耗时
//状态转移方程:DP[i][j] = min(DP[i][j], DP[i - 1][j - k] + t[k])
int main() {
	scanf("%d", &T);
	while (T--) {
		memset(DP, 0x3f3f3f, sizeof(DP));
		scanf("%d%d%d", &N, &M, &K);
		DP[0][0] = 0;
		for(int i = 1; i <= N; i++) {//预处理
			memset(t, 0x3f3f3f, sizeof(t));
			memset(pre, 0, sizeof(pre));
			scanf("%s", s + 1);
			int num = 0;//记录当天最多产生多少怒气
			for (int j = 1; j <= M; j++) {
				pre[j] = pre[j - 1];//pre记录到当前位置为止有多少个‘1’
				if (s[j] == '1')num++, pre[j]++;
			}
			for (int l = 1; l <= M; l++)
				for (int r = l; r <= M; r++) {
					int n = pre[r] - pre[l - 1];//区间[l, r]内‘1’的个数,则num - n为产生怒气值
					t[num - n] = min(t[num - n], r - l + 1);
				}
			t[num] = min(t[num], 0);
			for (int j = K; j >= 0; j--)
				for (int k = 0; k <= num; k++)
					if (j >= k)DP[i][j] = min(DP[i][j], DP[i - 1][j - k] + t[k]);
		}
		int ans = INF;
		for (int i = 0; i <= K; i++)
			ans = min(ans, DP[N][i]);
		printf("%d\n", ans);
	}
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值