Zut_round 2 (CF #486 DIV3)

本文详细解析了CF#486DIV3的六道题目,包括A到F题的题意、思路及代码实现,涵盖了数组处理、字符串排序、集合操作、动态规划等算法技巧。

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

CF #486 DIV3

A

题意:给数组a,从a中找出k个不一样的数。

思路:桶一下即可

#include<bits/stdc++.h>
using namespace std;
int vis[200];
vector<int>v;
int main(){
    int n,k;scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        int sc;scanf("%d",&sc);
        if(!vis[sc])
            vis[sc]=1,v.push_back(i);
    }
    if(v.size()>=k){
        puts("YES");for(int i=0;i<k;i++)printf("%d ",v[i]);
    }
    else puts("NO");
	return 0;
}

B

题意:给n个字符串si,问reorder后是否可以保证每一个si是后si+1的子串。

思路:相同长度的字符串一定是相同的,否则长的包含短的,那么只需要排序后判断一下就行了。

#include<bits/stdc++.h>
using namespace std;
string a[105];
bool cmp(string x,string y){return x.length()<y.length();}
bool check(string x,string y){
    int lx=x.length(),ly=y.length();
    for(int i=0;i<ly;i++){
        if(i+lx-1>=ly)continue;
        int tag=1;
        for(int j=0;j<lx;j++){
            if(x[j]!=y[i+j]){tag=0;break;}
        }
        if(tag)return 1;
    }
    return 0;
}
int main(){
    int n;cin>>n;for(int i=0;i<n;i++)cin>>a[i];sort(a,a+n,cmp);
    int tag=1;
    for(int i=0;i<=n-2;i++){
        if(check(a[i],a[i+1])==0){tag=0;break;}
    }
    if(tag) {
        puts("YES");
        for(int i=0;i<n;i++) cout<<a[i]<<'\n';
    }
    else puts("NO");
	return 0;
}

C

题意:给k个数组,挑出两个数组,使得这两个数组分别删去一个元素后和相同。

思路:枚举每一个可以得到的和,将可以得到和放入set,然后每次得到新的和,去set里找是否可以得到答案。时间复杂度O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int MX=2e5+5;
struct no{int h,l,s;};
bool operator<(const no &x,const no &y){
    return x.s<y.s;
}
set<no>se;set<no>::iterator it;
int a[MX];
int q,w,e,r;
int main(){
    int k;scanf("%d",&k);for(int j=1;j<=k;j++){
        int n;scanf("%d",&n);int sum=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);sum+=a[i];
        }
        for(int i=1;i<=n;i++){
            int now=sum-a[i];it=se.find(no{0,0,now});
            if(it!=se.end()){
                q=it->h,w=it->l,e=j,r=i;
            }
        }
        for(int i=1;i<=n;i++)se.insert(no{j,i,sum-a[i]});
    }
    if(q)printf("YES\n%d %d\n%d %d",q,w,e,r);
    else puts("NO");
	return 0;
}

D

题意:给n个在x轴上的点,从中挑出尽量多的数,使得任意两两之间的距离是2的倍数。

思路:最多选三个数,且是等差数列,那么sort之后枚举那个差就行了。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX=2e5+5;
LL a[MX];
int main(){
    int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    sort(a+1,a+1+n);
    int ans=0,book[5];
    for(LL i=1;i<=INT_MAX;i=i*2){
        for(int j=1;j<=n;j++){
            vector<int>v;v.clear();v.push_back(a[j]);
            int id1=lower_bound(a+1,a+1+n,a[j]-i)-a;
            if(id1!=n+1&&a[id1]==a[j]-i)v.push_back(a[id1]);
            id1=lower_bound(a+1,a+1+n,a[j]+i)-a;
            if(id1!=n+1&&a[id1]==a[j]+i)v.push_back(a[id1]);
            if(v.size()>ans){
                ans=v.size();
                for(int i=0;i<v.size();i++) book[i]=v[i];
            }
        }
    }
    printf("%d\n",ans);
    for(int i=0;i<ans;i++)printf("%d ",book[i]);
	return 0;
}

E

题意:给一个整数n,相邻两位可以交换,但是交换过程中不能有前导零,求最小的交换次数使得n可以整除25.

思路:可以整除25的时候,最后两位是 {00,25,50,75},那么4种都去考虑一下是否可行,对于每一次考虑,先决定个位,再决定十位。注意前导零的处理。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int MX=2e5+5;
char zzz[4][2]={'2','5','5','0','7','5','0','0'};
int main(){
    string n;cin>>n;int ln=n.length();
    int ans=-1;
    for(int i=0;i<4;i++){
        int u[2]={-1,-1};string nn=n;
        for(int j=0;j<2;j++){
            for(int k=ln-1-j;k>=0;k--){
                if(nn[k]==zzz[i][j^1]){
                    u[j]=k;
                    for(int z=k;z<ln-1-j;z++){
                        nn[z]=nn[z+1];
                    }
                    nn[ln-1-j]=zzz[i][j^1];
                    break;
                }
            }
        }
        if(u[0]!=-1&&u[1]!=-1){
            int tt=ln-2-u[0]+ln-1-u[1];
            if(nn[0]=='0'){//如果第一位被拿走了
                int p;
                for(p=0;p<ln;p++){
                    if(nn[p]!='0')break;
                }
                if(p<=ln-2) tt=tt+p;
                else continue;
            }
            if(ans==-1)ans=tt;
            else ans=min(ans,tt);
        }
    }
    cout<<ans;
	return 0;
}

F

题意:ly生活在一条直线上,ly在点0,要去点a,其中n个连续的区间在下雨,有m个点有雨伞,ly需要到有雨伞的地方拿伞,在下雨的区间需要有伞,伞有重量,求最小的ly的疲劳值(伞的重量*拿伞的路程)  (其实有更简单的解法。。。

思路:定义dp[ly的位置][拿的伞的重量],发现肯定开不下,发现最多2000*2000个状态,就用map离散化dp。

一个点一个点的走,有雨没雨,有伞没伞分类讨论即可..

注意:1.可能一个点有多个伞!!!(wa了好几次  2.注意处理下雨的区间和拿伞的时间点,我定义是经过这个点后达到的状态

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 2005;
map<int,int>mp[2];bool rain[MX];int un[MX];
int main(){//规定走到[l,r)时有雨,伞在点上,经过这个点
    int a,m,n;cin>>a>>n>>m;
    while(n--){
        int l,r;scanf("%d%d",&l,&r);
        for(int i=l;i<r;i++)rain[i]=1;
    }
    for(int i=1;i<=m;i++){
        int sc1,sc2;scanf("%d%d",&sc1,&sc2);
        if(un[sc1])un[sc1]=min(sc2,un[sc1]);
        else un[sc1]=sc2;
    }
    int now=0;mp[now][0]=0;
    for(int i=0;i<=a;i++){
        if(mp[now].size()==0){puts("-1");return 0;}
        mp[now^1].clear();
        int mi=INT_MAX; //找一个最小的值
        for(auto it:mp[now]){//继承前面的状态
            mi=min(mi,it.second);
            if(it.first==0&&rain[i]){
                continue;
            }
            mp[now^1][it.first]=it.second+it.first;
        }
        if(rain[i]==0) mp[now^1][0]=mi; //扔掉伞
        if(un[i])mp[now^1][un[i]]=mi+un[i];//捡起伞
        now=now^1;
    }
    int ans =INT_MAX;
    for(auto it:mp[now]) ans=min(ans,it.second);
    cout<<ans;
    return 0;
}

别人的解法:

思路:考虑每一个下雨的区间,去寻找最佳的拿伞的地方。 

dp[i]=dp[i-1](无雨) dp[i]=dp[j-1]+(i-j+1)*un[j] (有伞的地方转移过来) 注意i==0和j==0时候数组越界状态无法记录的情况。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int MX = 2005;
int dp[MX];bool rain[MX];int un[MX];
int main(){//规定走到[l,r)时有雨,伞在点上,经过这个点
    int a,m,n;cin>>a>>n>>m;
    while(n--){
        int l,r;scanf("%d%d",&l,&r);
        for(int i=l;i<r;i++)rain[i]=1;
    }
    for(int i=1;i<=m;i++){
        int sc1,sc2;scanf("%d%d",&sc1,&sc2);
        if(un[sc1])un[sc1]=min(sc2,un[sc1]);
        else un[sc1]=sc2;
    }
    for(int i=0;i<=a;i++){
        dp[i]=INT_MAX/2;
        if(!rain[i]){
            dp[i]=((i==0)?0:dp[i-1]);
        }
        else{
            for(int j=0;j<=i;j++){
                if(un[j]){
                    dp[i]=min(dp[i],(j==0?0:dp[j-1])+(i-j+1)*un[j]);
                }
            }
            if(dp[i]==INT_MAX/2){puts("-1");return 0;}
        }
    }
    cout<<dp[a];
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值