Codeforces Round #579 (Div. 3) 题解

本文解析了多项算法竞赛题目,包括圆排列、火柴矩形、公因数计数、子序列删除、拳击手数量优化及项目完成策略。通过深入分析与代码示例,展示了如何使用贪心算法、动态规划、排序与数学技巧解决复杂问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

A.Circle of Students
题意:给你1个1到n的排列,看能不能顺时针或者逆时针围城一个圈

做法可以向前向后判断,或者看一个两倍数组,检测有没有顺序或者逆序排列

B.Equal Rectangles
题意: 给你一堆火柴,问你能不能围成n个大小相等的矩形

思路: 排序后把最大和最小长度的火柴组一起就行,要注意判断给的火柴数目是不是偶数

C.Common Divisors
题意:给你n个数,要你求这n个数的公因数的个数

思路:求出这n个数的最大公约数,然后算这个数的因子个数,就是这n个数的公因数个数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=4e5+5;
ll a[MAX];
ll GCD(ll a, ll b){
    if(b==0){
        return a;
    }
    return GCD(b,a%b);
}
ll countt(ll n){
    ll s=1;
    for(ll i=2;i*i<=n;i++){
        if(n%i==0){
            ll a=0;
            while(n%i==0){
                n/=i;
                a++;
            }
            s=s*(a+1);
        }
    }
    if(n>1) s=s*2;
    return s;
}
 
int main (){
    int n;
    cin>>n;
    ll gcd=0;
    for(int i=0;i<n;i++){
        scanf("%lld",&a[i]);
        if(i>0)
            gcd=GCD(a[i],gcd);
        else
            gcd=a[i];
    }
    cout<<countt(gcd)<<endl;
    return 0;
}

D1 D2 . Remove the Substring
题意: 给你两个字符串s1,s2 问你从s1中能删去的最长字串长度,删去后使得s2仍然是s1的子序列

思路:能删去的最长长度分为3中情况,一种是s2第一个字符在s1序列中最后出现的位置,或者最后一个字符在s1序列中第一次出现的位置,或者同一种字符在中间的最大间隔

#include <bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;
int a[MAX],b[MAX];
int main(){
	string s1,s2;
	cin>>s1>>s2;
	int len1=s1.length(),len2=s2.length();
	int j=0;
	for(int i=0;i<len2;i++){
		while(s1[j]!=s2[i]) j++;
		a[i]=j;         //左边遍历找到最先出现的位置 
		j++;
	} 
	j=len1-1;
	
	for(int i=len2-1;i>=0;i--){
		while(s1[j]!=s2[i]) j--;
		b[i]=j;       //右边遍历找到最后出现的位置 
		j--;
	}
	int ans= max (ans , max (len1-1-a[len2-1],b[0])); // 找到左边空间或者右边空间最大的那一个
	
	for(int i=0;i<len2;i++){
		ans= max (ans,(b[i+1]-a[i]-1));     //把第一个出现的和最后一个出现的后面那一个找到
											// 减1是因为最后一个不能删 
	} 
	cout<<ans<<endl; 
	
	
	
	return 0;
} 

E.Boxers
题意:给你n个数,每个数只能加1或者减1,问你改变后最多有多少个不相等的数
思路:从大到小排序,然后用一个数组来维护一个数是否出现过,首先看这个数+1是否出现过,否则不变,如果不变的话也出现过就令他减1,最后再遍历一遍看有多少个数被数组标记过

#include <bits/stdc++.h>
using namespace std;
bool cmp(const int a,const int b){
    return a>b;
}
const int MAX=150000+5;
int a[MAX],ex[MAX];
int main (){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    sort(a,a+n,cmp);
    a[0]++;
    ex[a[0]]=1;
    for(int i=1;i<n;i++){
        if(ex[a[i]+1]!=1){
            a[i]++;
        }
        else {
            if(ex[a[i]]==1){
                if(ex[a[i]-1]==0&&a[i]>1){
                    a[i]--;
                }
            }
        }
        ex[a[i]]=1;
    }
    int ans=0;
    for(int i=0;i<MAX;i++){
        if(ex[i]==1){
            ans++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

F1.Complete the Projects (easy version)
题意:给定n个项目,和一个初始权值r。每个项目有两个数值构成,rating表示要进行这个项目的最低权值需求,get表示进行完这个项目后权值将会变化的数值。询问能否完成所有的项目。

思路:贪心。分开保存get为正和负的项目,正的按rating从小到大排序后直接取。负的需要按rating+get排序。详细证明与说明可以看这篇博客https://www.cnblogs.com/st1vdy/p/11352620.html

#include <bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;
struct node {
	int  rating;
	int  get;
}a[MAX],t1[MAX],t2[MAX];
bool cmp(const struct node a ,const struct node b){
	return a.rating<b.rating;
}
bool cmpp(const struct node a,const struct node b){
	return a.rating+a.get>b.rating+b.get;
}
int main (){
	int n,r;
	cin>>n>>r;
	int k=0,kk=0;
	for(int i=0;i<n;i++){
		cin>>a[i].rating>>a[i].get;
		if(a[i].get>=0) t1[k++]=a[i];
		else t2[kk++]=a[i];
	}
	sort(t1,t1+k,cmp);
	sort(t2,t2+kk,cmpp);
	int flag=0;
	for(int i=0;i<k;i++){
		if(r<t1[i].rating){
			flag=1;
			break;
		}
		else r+=t1[i].get;
	}
	if(flag){
		cout<<"NO"<<endl; return 0;
	}
	for(int i=0;i<kk;i++){
		if(r<t2[i].rating){
			flag=1;
			break;
		}else {
			r+=t2[i].get;
		}
		if(r<0){
			flag=1;
			break;
		}
	}
	if(flag){
		cout<<"NO"<<endl; 
	}
	else 
	cout<<"YES"<<endl;
	
	return 0;
} 

F2.Complete the Projects (hard version)
与F1 不同的地方在于,我们需要尽量多地完成项目,在取正数部分和F1做法一样,在负数部分,我们既要尽可能的完成,又要尽量的使权值r尽量减得更少,就变成了一个dp

#include <bits/stdc++.h>
using namespace std;
const int MAX=1e5+5;
struct node {
	int  rating;
	int  get;
}a[MAX],t1[MAX],t2[MAX];
int dp[MAX];
bool cmp(const struct node a ,const struct node b){
	return a.rating<b.rating;
}
bool cmpp(const struct node a,const struct node b){
	return a.rating+a.get<b.rating+b.get;
}
int main (){
	int n,r;
	cin>>n>>r;
	int k=0,kk=0;
	for(int i=0;i<n;i++){
		cin>>a[i].rating>>a[i].get;
		if(a[i].get>=0) t1[k++]=a[i];
		else t2[kk++]=a[i];
	}
	sort(t1,t1+k,cmp);
	sort(t2,t2+kk,cmpp);
	int flag=0;
	int ans=0;
	for(int i=0;i<k;i++){
		if(r<t1[i].rating){
			flag=1;
			break;
		}
		else {
			r+=t1[i].get;
			ans++;
		}
	}
	for(int i=0;i<kk;i++){
		for(int j=r;j>=abs(t2[i].get)&&j>=t2[i].rating;j--){
			dp[j]=max(dp[j],dp[j+t2[i].get]+1);
		}
	}
	cout<<ans+dp[r]<<endl;
	return 0;
} 

dp[j]表示权值剩余j的时候完成的项目个数,所以它等于不完成当前项目或者完成当前项目权值减少get ,项目数+1 的最大值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值