2022牛客寒假算法基础集训营4

本文档涵盖动态规划在A-R问题中的应用,通过代码解析展示了如何解决R元素的计数问题。同时介绍了雪色光晕的几何问题,涉及点到线段最短距离计算。还有小红记谱法和爆炸符卡问题的动态规划解法,以及区间合数最小公倍数的算法。最后,以小红签到题为例,探讨思维题目的解答技巧。
摘要由CSDN通过智能技术生成

A-R(动态规划)

题解

动态规划,对于dp[i]表示第i个数有多少种合法方案。然后找关系式,包含第i个数的所有方案加上dp[i-1]就是答案,那么包含第i个数的所有方案怎么算呢?
首先必须有K个R,如果没有肯定为0,如果有,我们发现我们必须要从后往前找到R等于K的那个下标,然后从那个下标到起始或者P的位置的距离就是我们的答案

代码

#include<iostream>
using namespace std;
long long dp[200200];
string s;
int n,k;
long long a[200200];//R的个数
long long v[200200];//R个数对应下标
int main(){
    cin>>n>>k>>s;
    int pr=-1,pp=-1;
    for(int i=0;i<n;i++){
        a[i]=a[i-1];
        if(s[i]=='R'){
            a[i]++;
            v[a[i]]=i;
            //cout<<"v[a[i]]"<<v[a[i]]<<endl;
            if(a[i]>=k) pr=v[a[i]-k+1];
            //cout<<"pr="<<pr<<" a[i]="<<v[a[i]-k-1]<<endl;
        }else if(s[i]=='P'){
            pr=-1,pp=i,dp[i]=dp[i-1],a[i]=0;
            continue;
        }
        if(a[i]>=k){
            if(pp==-1) dp[i]=dp[i-1]+pr+1;
            else dp[i]=dp[i-1]+pr-pp;
        }else dp[i]=dp[i-1];
        //cout<<" i="<<i<<" dp[i]="<<dp[i]<<endl;
    }
    cout<<dp[n-1];
    return 0;
}

D-雪色光晕(几何问题)

题解

点到线段的最短距离

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
	double n,x,y,x0,y0,x1,y1,ans;
	double c;
	cin>>n>>x0>>y0>>x>>y;
	ans=sqrt((x0-x)*(x0-x)+(y0-y)*(y0-y));
	for(int i=1;i<=n;i++){
		cin>>x1>>y1;
		if(x1*x1+y1*y1!=0){
        //AC向量
		c=(-x1*(x0-x)+y1*(y-y0))/(x1*x1+y1*y1);
        //<=0 d=AP <=1 d=cp >1 d=bp
		if(c<=0) ans=min(ans,sqrt((x0-x)*(x0-x)+(y0-y)*(y0-y)));
		else if(c>=1) ans=min(ans,sqrt((x0+x1-x)*(x0+x1-x)+(y0+y1-y)*(y0+y1-y)));
		else  ans=min(ans,sqrt((x0+c*x1-x)*(x0+c*x1-x)+(y0+c*y1-y)*(y0+c*y1-y)));
		}
		x0+=x1,y0+=y1;
	}
    printf("%.8lf",ans);
}

F-小红记谱法

题解

‘<’ = -1 ‘>’ = 1 ,0表示不变,-1表示低1,1表示高1

代码

#include<iostream>
#include<stack>
#include<map>
using namespace std;
string s;
int p[8]={6,7,1,2,3,4,5};
int a[1100],ans1[1100],ans2[1100];//ans1为数字,ans2为关系
//< = -1  > = 1 ,0不变,-1低,1高
int main(){
    cin>>s;
    int k=0,ans=0;
    for(int i=0;i<s.size();i++){
        if(s[i]=='<') k-=1;
        else if(s[i]=='>') k+=1;
        else{
            int x=s[i]-'A';
            ans1[++ans]=x;
            ans2[ans]=k;
        }
    }
    
    for(int i=1;i<=ans;i++){
        cout<<p[ans1[i]];
        if(ans2[i]==0) continue;
        else if(ans2[i]<0){
            while(ans2[i]<0){
                ans2[i]+=1;
                cout<<'.';
            }
        }else{
            while(ans2[i]>0){
                ans2[i]-=1;
                cout<<'*';
            }
        }
    }
    
    return 0;
}

I-爆炸的符卡洋洋洒洒(动态规划)

题解

因为我们需要的答案是K的倍数,根据同余定理,我们可以给所有数取余,然后余数为0的最大值就是我们的答案,写出关系式:
dp[i][j]=max(dp[i-1][j],dp[i-1][(j-a[i]+k)%k]+b[i])

注意由于我们取余,dp[i-1][(j-a[i]+k)%k]在进行计算的时候也许会将一组不存在的符卡组合加上我们当前的符卡从而得出错误答案,所以需要将其初始化为最小值,对于不存在的符卡就算加上了b[i]也将是负数。

代码

#include<iostream>
#include<string.h>
using namespace std;
long long dp[1100][1100],a[1100],b[1100];
int main(){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i]>>b[i],a[i]=a[i]%k;
    memset(dp,-0x3f,sizeof dp);
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        for(int j=k-1;j>=0;j--){
            dp[i][j]=max(dp[i-1][j],dp[i-1][(j-a[i]+k)%k]+b[i]);
            //cout<<dp[i][j]<<" ";
        }
        //cout<<endl;
    }
    if(dp[n][0]) cout<<dp[n][0];
    else cout<<-1;
}

J-区间合数的最小公倍数(多个数的最小公倍数)

题解

最小公倍数等于质数出现的最高次幂之和
我们对于每个回合数找出所有它的质因子记录下它们的幂,然后选择最大值,最后将所有质因子的最高次幂相乘

代码

#include<bits/stdc++.h>
using namespace std;
int cnt[30030];
const int mod = 1e9+7;
bool isp(int x){
    for(int i=2;i*i<=x;i++)
        if(x%i==0) return 1;
    return 0;
}

int solve(int x,int k){
    int ans=1;
    while(k--) ans=ans*x%mod;
    return ans;
}

int main(){
    int l,r,i;
    cin>>l>>r;
    for(int j=l;j<=r;j++){
        if(isp(j)){//如果是合数
            int x=j;
            for(int i=2;i*i<=j;i++){
                int k=0;
                while(x%i==0){
                    k++;
                    x/=i;
                }
                cnt[i]=max(cnt[i],k);
            }
            if(x!=1) cnt[x]=max(cnt[x],1);
        }
    }
    bool A=0;
    long long ans=1;
    for(int i=2;i<=r;i++){
        if(cnt[i]){
            A=1;
            ans=ans*solve(i,cnt[i])%mod;
        }
    }
    if(A) cout<<ans%mod;
    else cout<<-1;
    return 0;
}

K-小红的真真假假签到题题(思维)

题解

写几个数以及最小的答案的二进制观察一下
比如 5 45
101 101101
你会发现最小答案的二进制就是数x的二进制在拼接了一个数x的二进制。
这是为什么?
其实也好想,首先答案必须是X的倍数,那它必须要左移,又要求1的个数不一样,那怎么样左移加上怎么的1能成为X的倍数呢
肯定要X左移以后再加上X的二进制,这就绝对成为了X的倍数,那这样需要左移最小次数也出来了,就是X二进制的位数

代码

#include<iostream>
using namespace std;

int main(){
    int n;
    cin>>n;
    int x=n;
    long long k=1;
    while(n){
        k*=2;
        n/=2;
    }
    k=k*x+x;
    cout<<k;
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值