第四届挑战赛二轮题解

A .连击(string)

题目分析

本题考场字符串或者数组,直接模拟即可,判断每一个位置和它的后两个位置是否都是o即可,不需要判断边界条件,因为最后一个字符后面的都是空的,也不会满足条件,注意在查到输出Yes后要结束,如果没结束就是没找到,输出No

复制代码
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
int main(){
    cin>>n>>s;
    for(int i=0;i<n;i++)
    {
        if(s[i]=='o'&&s[i+1]=='o'&&s[i+2]=='o')
        {
            cout<<"Yes";
            return 0;//结束
        }
    }
    cout<<"No";
    return 0;
}

B.卡牌(card)

题目分析

选出的数奇偶性两两相等,意味着只能全部选奇数或全部选偶数。因此可以将原数列的奇数和偶数分别挑出来存在两个数组中,分别对两个数组从大到小排序并分别算出前 kkk 大的奇数之和和前 kkk 大的偶数之和分别记为 s1s_1s1 和 s2s_2s2 。需要特别注意的是若奇数数列(或偶数数列)总长度小于 kkk,则s1s_1s1 或 s2s_2s2要设为 0,然后输出 max(s1,s2)max(s_1,s_2)max(s1,s2) 即可。

复制代码
#include<bits/stdc++.h>
using namespace std;
long long a[1000001],o[1000001],e[1000001],h1=0,h2=0,anso=0,anse=0,lo=0,le=0;
int cmp(int a,int b){
	return a>b;
}
int main(){
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]%2==1){
			o[h1++]=a[i];
			lo++;
		}
		else{
			e[h2++]=a[i];
			le++;
		}
	}
	sort(o,o+h1,cmp);
	sort(e,e+h2,cmp);
	for(int i=0;i<k;i++){
		anso+=o[i];
		anse+=e[i];
	}
	if(lo<k){
		anso=0;
	}
	if(le<k){
		anse=0;
	}
	cout<<max(anso,anse);
	return 0;
}

C. 楼梯(stair)

题意

给你一个数组,你可以将其中的值+1,-1或不变,若能通过这种改变使改数组成为等差数组,则输出改变值的次数,若不能,则输出-1。

题目分析

本题要求将数组变为等差数组,因此可以通过固定前两个元素的值,计算出公差后,再判定之后的元素是否能变成等差数组。前两个数的修改情况确定后,后面的数组就确定了。

因此,枚举前两个数的修改情况,再循环判定数组的修改即可。

要注意n=1,n=2n=1,n=2n=1,n=2单独处理下。

复制代码
#include <bits/stdc++.h>
using namespace std;

int n,b[100001],ans = 1e9,a[100001],now;
int o[3] = {1,-1,0};//每种情况

int check() {
	int c = b[2] - b[1],num = 0;//差值和修改次数
	for(int i = 1; i <= n; i++) a[i] = b[i]; //备用数组
	for(int i = 3; i <= n; i++) {
		int nc = a[i] - a[i - 1];//新的差
		if(nc != c) { //需要修改
			if(nc + 1 == c) a[i]++;
			else if(nc - 1 == c) a[i]--;
			else return 1e9;//无法满足条件
			num++;//修改次数加一
		}
	}
	return num;
}
int work() {
	ans = 1e9;
	scanf("%d",&n);
	for(int i = 1; i <= n; i++) scanf("%d",&b[i]);
	if(n <= 2) {
		printf("0\n");    //长度小于二就一定可以
		return 0;
	}
	for(int i = 0; i < 3; i++) { //枚举情况
		for(int j = 0; j < 3; j++) {
			b[1] += o[i];
			b[2] += o[j];//修改前两个
			now = 0;
			if(i < 2) now++;//记得要增加修改次数
			if(j < 2) now++;
			ans = min(ans,check() + now);//取最小值
			b[1] -= o[i];//改回去
			b[2] -= o[j];
		}
	}
	if(ans == 1e9) printf("-1\n");//不可以
	else printf("%d\n",ans);
	return 0;
}
signed main() {
	int t;
	scanf("%d",&t);
	while(t--) {

		work();
	}
	return 0;
}

D .养花(flowers)

题意

但综合起来就一句话:给你 NNN 个范围,每个范围有一个权值且不重复;再给你 MMM 个东西,每个东西可以使范围内的点权值减pip_ipi,但要付出 mim_imi 点代价,求使得所有点的权值均不大于 000 时,最少付出多少代价。

题目分析

估计一看到数据范围,容易想到暴力搜索,每种肥料有买或者不买两种可能,我们通过搜索回溯或者子集枚举都可以,选出一种方案后判断是否符合条件即可。

dfs写法

复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=25,M=110; 
int n,m,s[N],t[N],c[N],a[N],b[N],p[N],l[N],ans=0x3f3f3f3f,k[M];
bool check(){
   for(int i=1;i<M;i++){
   	if(k[i]>0)
   		return false;
   }
   return true;
}
void dfs(int i,int dollar){
   if(i==m+1){
   	if(check()){
   		ans=min(ans,dollar);
   	}
   	return;
   }
   dfs(i+1,dollar);
   for( int j=a[i];j<=b[i];++j){
   	k[j]-=p[i];
   }
   dfs(i+1,dollar+l[i]);
   for( int j=a[i];j<=b[i];++j){
   	k[j]+=p[i];
   }
}
int main(){
   cin>>n>>m;
   for(register int i=1;i<=n;++i){
   	cin>>s[i]>>t[i]>>c[i];
   	for(int j=s[i];j<=t[i];j++)
   		k[j]=c[i];
   }
   for(int i=1;i<=m;++i)
   	cin>>a[i]>>b[i]>>p[i]>>l[i];
   dfs(1,0);
   cout<<ans;
   return 0;
}

E.整除(count)

题目分析

观察数据范围,容易想到二分答案。

那么问题转化为如何统计 1∼k1 \sim k1∼k 中的美丽数的数量。

考虑 1∼k1 \sim k1∼k 中有多少个数能被 ppp 整除,答案显然是 ⌊k/p⌋\lfloor k/p \rfloor⌊k/p⌋。

考虑 1∼k1 \sim k1∼k 中有多少个数能被 ppp qqq 整除,答案是 ⌊k/p⌋+⌊k/q⌋\lfloor k/p \rfloor + \lfloor k/q \rfloor⌊k/p⌋+⌊k/q⌋ 吗?

不对,因为这样的话,能同时被 ppp 和 qqq 整除的数会被算两次。所以要减去能同时被 ppp 和 qqq 整除的数,也就是减去 ⌊k/lcm(p,q)⌋\lfloor k / \text{lcm}(p,q)\rfloor⌊k/lcm(p,q)⌋。

考虑 1∼k1 \sim k1∼k 中有多少个数能被 ppp qqq 整除,那么就是再减去能被 ppp 和 qqq 同时整除的数。

所以 1∼k1 \sim k1∼k 中美丽数的数量就是 ⌊k/p⌋+⌊k/q⌋−2⌊k/lcm(p,q)⌋\lfloor k / p \rfloor + \lfloor k / q \rfloor - 2\lfloor k/\text{lcm}(p,q)\rfloor⌊k/p⌋+⌊k/q⌋−2⌊k/lcm(p,q)⌋。

这样我们就可以直接二分得到最后的答案。

c++ 复制代码
#include <iostream>
#define int long long
using namespace std;

int check (int p, int n, int m, int g)
{
	return p / n + p / m - p / g * 2;
}

int gcd (int a, int b)
{
	if (!b) return a;
	return gcd (b, a % b);
}

signed main (void)
{
	int t;


	cin.tie(0)->sync_with_stdio (false);
	cin >> t;
	while (t--) {
		int n, m, k;

		cin >> n >> m >> k;
		int g = n / gcd (n, m) * m;
		int l = 1, r = 1e18, mid, ans = -1;
		while (l <= r) {
			mid = (l + r) / 2;
			if (check (mid, n, m, g) < k) l = mid + 1;
			else r = (ans = mid) - 1;
		}
		cout << ans << '\n';
	}
	return 0;
}

F.权衡(gaokao)

题目分析

考虑这其实和背包问题很像,只是本题有多个"价值"。

然而价值都很小(最大为 666),那么我们可以直接把它放到我们的 DP 状态里。

所以最终的状态就是:fiabcdeff_{iabcdef}fiabcdef,表示考虑了前 iii 种学习计划,每种科目的得分分别为 a,b,c,d,e,fa, b, c, d, e, fa,b,c,d,e,f。

其实还有更简单的写法:把 abcdefabcdefabcdef 压到同一个整数里,也就是用一个六进制数来作为状态。

转移和背包问题是一样的。

c++ 复制代码
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int n, k;
ll f[2][7][7][7][7][7][7];
int a[10];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n >> k;

	memset(f, 0x3f, sizeof(f));
	ll inf = f[0][0][0][0][0][0][0];
	f[0][0][0][0][0][0][0] = 0;

	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= 6; ++j) cin >> a[j];

		ll c;
		cin >> c;
		memcpy(f[i & 1], f[(i - 1) & 1], sizeof(f[i & 1]));

		for (int a1 = 0; a1 <= 6; ++a1) {
			for (int a2 = 0; a2 <= 6; ++a2) {
				for (int a3 = 0; a3 <= 6; ++a3) {
					for (int a4 = 0; a4 <= 6; ++a4) {
						for (int a5 = 0; a5 <= 6; ++a5) {
							for (int a6 = 0; a6 <= 6; ++a6) {
								f[i & 1][min(a1 + a[1], k)][min(a2 + a[2], k)][min(a3 + a[3], k)][min(a4 + a[4], k)][min(a5 + a[5], k)][min(a6 + a[6], k)] = min(f[(i - 1) & 1][a1][a2][a3][a4][a5][a6] + c, f[i & 1][min(a1 + a[1], k)][min(a2 + a[2], k)][min(a3 + a[3], k)][min(a4 + a[4], k)][min(a5 + a[5], k)][min(a6 + a[6], k)] );
							}
						}
					}
				}
			}
		}
	}

	if (inf == f[n & 1][k][k][k][k][k][k]) return puts("-1"), 0;

	cout << f[n & 1][k][k][k][k][k][k];

	return 0;
}
相关推荐
dragoooon341 小时前
[优选算法专题十一.字符串 ——NO.60~63 最长公共前缀、5最长回文子串、 二进制求和 、字符串相乘]
算法·leetcode·动态规划
小毅&Nora1 小时前
【后端】【C++】函数对象与泛型算法:从“找最便宜的菜”说起
c++·算法·泛型
CoderYanger1 小时前
C.滑动窗口——2762. 不间断子数组
java·开发语言·数据结构·算法·leetcode·1024程序员节
2401_837088502 小时前
Integer.MIN_VALUE 是什么意思?
java·开发语言·算法
好风凭借力,送我上青云2 小时前
哈夫曼树和哈夫曼编码
c语言·开发语言·数据结构·c++·算法·霍夫曼树
程序员-King.2 小时前
day118—二分查找—咒语和药水的成功对数(LeetCode-2300)
算法·leetcode·二分查找
小O的算法实验室2 小时前
2025年COR SCI2区,双种群 NSGA-II 算法+卡车–无人机–调度车辆的多目标应急物资调度,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
KiefaC2 小时前
【C++】红黑树的调整
开发语言·c++·算法
第二只羽毛2 小时前
C++高性能内存池
开发语言·c++·缓存·性能优化