郑州轻工业大学2020级新生程序设计大赛题解


更新了 7- 2 的代码,之前有点小 bug
更新了 7 -10 的代码,/(ㄒoㄒ)/~~
这一次真的更新好了,可以放心观看
感谢强哥等等一系列大佬给我魔法药水这个题的思路

我好菜 /(ㄒoㄒ)/


题目地址:传送门~~~


7-1 小A与泉水

思路
位运算。在二进制下,从低位开始向上进位即可。
应该还有其他写法,没再去想了 (lll¬ω¬)
这个题好像卡输入和输出,别用 cin 和 cout

AC代码

#include <iostream>
#include <bits/stdc++.h>

using namespace std;

int main(int argc, char** argv) {
	
	int ncase;
	scanf("%d", &ncase);
	
	while(ncase--){
		int n;
		scanf("%d", &n);
		int res = 0;
		if(n == 0){
			printf("0\n");
			continue;
		}
		while(n){	// 在纸上模拟一下就OK了
			if(n & 1){
				res++;
				if(n != 1) n += 2; 
			}
			n >>= 1;
		}
		printf("%d\n", res-1);
	}
	
	return 0;
}

7-2 小A的魔法

思路
二维前缀和,容斥原理
~~~

黄色部分的权值 = {(7,9)->(1,1)} - {(2,9)->(1,1)} - {{(7,2)->(1,1)}} + {{(2,2)->(1,1)}}
ps: {(7,9)->(1,1)} 表示以这两个点为顶点的矩形的权值
统计权值,计算前缀和,再通过上边公式枚举所有点就OK

这里还有一种思路:
题目给的点数比较少,选取一个敌人所在的点作为矩形的一个顶点,由这个顶点确定四个矩形,再依次找在矩形内的点数有几个。
如果暴力找点的话,时间复杂度为O(n*n),妥妥的能过 手动狗头

AC代码

// 第一种思路的代码,第二种思路的代码自己试试吧
#include <iostream>
#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e3;

int v[maxn][maxn] = {0};

int main(int argc, char** argv) {
	
	int n, w, k;
	cin >> n >> w >> k;
	int x, y;
	for(int i = 1; i <= n; i++){
		cin >> x >> y;
		v[x][y]++;
	}
	
	for(int i = 1; i < maxn; i++){	// 前缀和
		for(int j = 1; j < maxn; j++){
			v[i][j] += v[i][j-1] + v[i-1][j] - v[i-1][j-1];
		}
	} 
	
	int res = 0; 
	
	for(int i = 1; i < maxn; i++){	// 找最值
		for(int j = 1; j < maxn; j++){
			int xx = i - w + 1;
			int yy = j - k + 1;
			if(xx <= 0 || yy <= 0) continue;
			int tmp = v[i][j] - v[xx-1][j] - v[i][yy-1] + v[xx-1][yy-1];
			res = max(res, tmp);
		}
	}
	
	cout << res << endl;
	
	return 0;
}

7-3 小A的宝藏

思路
博弈论。

巴什博弈
每次有至多取走 m 个硬币,至少取走 1 个硬币。
共有 (m+1) * k + x 个硬币 (x 小于 m)。
A先手,B后手,取走最后一个硬币者胜。
分析: 如果 A 先手取走 x 个硬币,还剩下 (m+1) * k 个硬币。接下来 B 取走 z 个硬币,这时 A 可以通过取走 m+1-z 个硬币保证剩下的硬币为 m+1 的倍数。如此 K 轮以后,我们会发现 A 取走了最后一个硬币。即 A 必胜。
but,当 x 为 0 时,A 必败,类比刚刚的分析(B 在面对 (m+1) * k 时,必败)。

看完这个我们就可以知道,本题内的 m 为 3,硬币的数量就是银子的数量。
当没有金子的时候,可以知道对面 3 * k 个银子的人必输
下边我们就来分析各个情况:

3 * k 个银子,0 个金子,先手败。
3 * k + 1 个银子,0 个金子,先手胜。先手拿走一个银子,后手面对 3 * k 个银子,必败。
3 * k + 2 个银子,0 个金子,先手胜。先手拿走两个银子,后手面对 3 * k 个银子,必败。
3 * k 个银子,1 个金子,先手胜。      先手拿走一个金子,后手面对 3 * k 个银子,必败。
3 * k + 1 个银子,1 个金子,先手败。无论先手拿走什么,后手都是必胜态。
3 * k + 2 个银子,1 个金子,先手胜。先手拿走一个银子,后手面对必败态。

最后,m 个 金子可以压缩成对应的 0 或 1。
这个题同样卡输入和输出
AC代码

#include <iostream>
#include <bits/stdc++.h>

using namespace std;

int main(int argc, char** argv) {
	
	int ncase;
	scanf("%d", &ncase);
	while(ncase--){
		long long a, b;
		scanf("%lld %lld", &a, &b);
		if(a % 3 == 2) printf("Alice\n");
		else if(a % 3 == 0){
			if(b & 1) printf("Alice\n");
			else printf("Bob\n");
		}
		else{
			if(b & 1) printf("Bob\n");
			else printf("Alice\n");
		}
	}
	
	return 0;
}

7-4 靶场射击2.0

思路:
字符串处理,统计每个字母出现的次数找最值即可。

AC代码

#include <iostream>
#include <bits/stdc++.h>

using namespace std;

int v[2][26] = {0};

int main(int argc, char** argv) {
	
	int n, a, b;
	cin >> n >> a >> b;
	int res = 0; char c;
	for(int i = 1; i <= n; i++){
		cin >> c;
		if(c >= 'a' && c <= 'z'){	// 普通 
			int tmp = c - 'a';
			v[0][tmp]++;
			res = max(res, v[0][tmp] * b + v[1][tmp] * a);
		}
		else{		// 特殊 
			int tmp = c - 'A';
			v[1][tmp]++;
			res = max(res, v[0][tmp] * b + v[1][tmp] * a);
		}
	}
	
	for(int i = 0; i < 26; i++){
		if(v[0][i] * b + v[1][i] * a == res){
			cout << (char)(i + 'A') << endl;
			break;
		}
	}
	
	return 0;
}

7-5 这是一个数学题

思路
思维题~~~
如果一个数是 20 的倍数那么最后两位数要为偶数和一个零。
把一个数往这个状态转移,看需要换几次即可。
如果达不到这个状态,结果就为 -1。

AC代码

#include <iostream>
#include <bits/stdc++.h>

using namespace std;

int main(int argc, char** argv) {
	
	int ncase;
	cin >> ncase;
	while(ncase--){
		long long n;
		cin >> n;
		if(n < 20){
			cout << -1 << endl;
			continue;
		}
		vector<int> v;
		while(n){
			int tmp = n % 10;
			v.push_back(tmp);
			n /= 10;
		}
		int res = 0;
		if(v[0] != 0){
			int f = 0;
			for(int i = 2; i < v.size(); i++){
				if(v[i] == 0){
					swap(v[0], v[i]);
					f = 1;
					res++;
					break;
				}
			}
			if(f == 0 && v[1] == 0){
				swap(v[0], v[1]);
				res++;
			}
			else if(f == 0) res = -1;
		}
		if(v[1] % 2 != 0 && res != -1){
			int f = 0;
			for(int i = 2; i < v.size(); i++){
				if(v[i] % 2 == 0){
					swap(v[1], v[i]);
					f = 1;
					res++;
					break;
				}
			}
			if(f == 0) res = -1;
		}
		cout << res << endl;
	}

	return 0;
}

7-6 代码格式化

思路
字符串处理。

AC代码

#include <bits/stdc++.h>
using namespace std;

string s, ss;

int main(){
	
	getline(cin, ss);
	getline(cin, s);

	cout << ss << endl;
	
	int len = s.size();
	for(int i = 0; i < len; i++){
		if(s[i] == '{') cout << " " << s[i] << endl << "    ";
		else if(s[i] == ';' && i+2 != len) cout << s[i] << endl << "    ";
		else if(s[i] == ';') cout << s[i] << endl;
		else if(s[i] == '=') cout << " " << s[i] << " ";
		else if(s[i] == '+') cout << " " << s[i] << " ";
		else if(s[i] == '-') cout << " " << s[i] << " ";
		else if(s[i] == '*') cout << " " << s[i] << " ";
		else if(s[i] == '/') cout << " " << s[i] << " ";
		else if(s[i] == '%') cout << " " << s[i] << " ";
		else cout << s[i];
	}
	
	cout << endl;
	
	return 0;
}

7-7 贪心的小H

思路
假设当前背包有 x 个物品,价值为 y,对于未放入背包价位为 val 物品 A:
如果我们把它放入背包,背包的价值变为 (x+1) * (y + val), 展开得 x*y + y + (x+1) * val。
贪心得,如果 y + (x+1) * val 大于零,我们就把它放入背包。
对所有物品,按照 val 排序,从大到小去判断即可。

AC代码

#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e5 + 5;

using namespace std;

typedef struct Node{
	int id;
	int val;
} node;

bool cmp(node A, node B){
	if(A.val == B.val) return A.id < B.id;
	return A.val > B.val;
}

node a[205];

int main(){
	
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i].id;
	for(int i = 1; i <= n; i++) cin >> a[i].val;
	
	sort(a+1, a+1+n, cmp);
	
	int x = 0, y = 0;
	for(int i = 1; i <= n; i++){
		if(a[i].id == a[i-1].id) continue;
		if(y + (x+1) * a[i].val > 0){
			x += 1;
			y += a[i].val;
		}
		else break;
	}
	
	cout << x * y << endl;
	
	return 0;
}

7-8 #define int long long

签到题。

AC代码

#include <bits/stdc++.h>
using namespace std;

int main(){
	
	for(int i = 1; i <= 10; i++) cout << "#define int long long" << endl;

	return 0;
}

7-9 小A的魔力

思路
按照题意取最大值对应的字符串即可。

AC代码

#include <bits/stdc++.h>
using namespace std;

int main(){
	
	int n;
	cin >> n;
	int mx = -1, tmp; 
	string res, s;
	for(int i = 1; i <= n; i++){
		cin >> s >> tmp;
		if(tmp > mx){
			mx = tmp;
			res = s;
		}
	}
	
	cout << res << endl;
	
	return 0;
}

7-10 魔法药水 (好题)

思路
二分答案,在贪心wa了一以后就感觉是二分答案,不过一直想不到check函数咋写。

答案要的是价值和大于等于 X 的最少药瓶数,优先取价值大的药水这样想肯定没问题。

本人第一想法:
依次取价值最大和次大的药水轮流放,然后被自己无情 hack
这里给出一组 hack 数据:
     4   67
     4   10
     3    5
     10   4
     10   1
答案是:10
其中一个满足的排列:10、4、5、10、4、5、10、4、5、10

下边来说说正解:
二分答案。每次取验证一个瓶子数是不是能满足题意,在所有满足题意里边取最优。
这里说一个点,对于第 i 种药水,它在一个需要 X 个药水的方案种能使用的最多药水数为:(X+1)/ 2
看图吧,应该看得懂对于 X 的奇偶性质,自己画个图数数就知道了
抽象派画手
其他的细节看代码,详解,保懂。

AC代码

#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e5 + 5;

using namespace std;

typedef struct Node{
	ll num;
	ll val;
} node;

bool cmp(node A, node B){	// 自定义排序,主要是按照价值排序,数量无所谓 
	if(A.val == B.val) return A.num > B.num;
	return A.val > B.val;
}

ll n, x;
node a[maxn];

int check(ll mid){	// check 函数 
	ll cnt = mid, sum = x;	// 有 mid 个 位置放药水,需要达到价值 sum 
	for(int i = 1; i <= n; i++){				// 遍历,提前排过序的 
		ll num = a[i].num, w = a[i].val;		// 第 i 种药水的数量 和 单个的价值 
		ll tmp = min({num, (mid+1)/2, cnt});	
		// 对于当前情况,这个药水能用的最多个数是:
		//	当前药水的数量、一个方案里一种药水最多可以放多少个位置、还需要的药水数。三者取 min。 
		sum -= tmp * w;	// 减去这个药水贡献的价值 
		cnt -= tmp;		// 减去这个药水贡献的数量 
		if(cnt == 0) break;	// 数量满足就提前结束 
	}
	if(cnt > 0) return 1;	// 药水不够填满 mid 个位置 
	if(sum <= 0) return 2;	// 药水可以填满 mid 个位置,并且满足价值大于 x 
	return 0;				// 不满足价值大于 x 
}

int main(){
	
	cin >> n >> x;
	for(int i = 1; i <= n; i++) cin >> a[i].num >> a[i].val;
	
	sort(a+1, a+1+n, cmp);		// 按权值排序,贪心思想,优先取权值大的 
	
	ll l = 1, r = 1e18, res = -1;	// 二分答案 
	
	while(l <= r){
		ll mid = l + r >> 1;
		int op = check(mid);
		if(op == 1) r = mid -1;
		else if(op == 2) r = mid - 1, res = mid;
		else l = mid + 1;
	}
	
	cout << res << endl;

	return 0;
}


写在最后,这次最后这个题是我的锅,以后再也不提前写题解了,脸疼(;´д`)ゞ
下 次 还 会


  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值