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;
}