2023河南CCPC省赛vp部分补题

发布于:2025-05-23 ⋅ 阅读:(16) ⋅ 点赞:(0)

A 模拟 暴力

对每个合法的前缀,判断后缀是不是合法

int a[29];
void solve(){
    string s;
    cin>>s;
    int t=-1;
    if(s.size()==1){
        return cout<<"NaN"<<endl,void();
    }
    for(int i=0;i<=27;i++)    a[i]=0;
    for(int i=0;i<s.size();i++){
        a[s[i]-'a']++;
        if(a[s[i]-'a']>1){
            t=i;
            break;
        }else{
            string ss=s.substr(i,s.size()-i);
            string s1=ss;
            reverse(ss.begin(),ss.end());
            if(ss==s1){
                return cout<<"HE"<<endl,void();
            }
            else continue;
        }
    }
    if(t==-1)t=s.size()-1;
    if(t==s.size()-1){
        return cout<<"HE"<<endl,void();
    }
    string ss=s.substr(t,s.size()-t);
    string s1=ss;
    reverse(ss.begin(),ss.end());
    if(ss==s1)    cout<<"HE"<<endl;
    else    cout<<"NaN"<<endl;
}

B 思维 枚举

题意:每k个数一组,每小组升序排序后能让整个排序变成升序
思路:

  • 若要满足题意,那么每个组的最大值要小于等于下一组的最小值,就是前缀最大值小于等于下一个数的后缀最小值,这就是组和组的边界
  • 标记每一个边界位置,枚举k,找符合题意的k的个数
void solve()
{
    int n;
    cin>>n;
    vector<int>a(n+1),pmax(n+1,0),smin(n+2,1e9);
    forr(i,1,n){
        cin>>a[i];
    }
    reforr(i,1,n){
        smin[i]=min(smin[i+1],a[i]);
    }
    vector<int>rec(n+1,0);
    forr(i,1,n){
        pmax[i]=max(pmax[i-1],a[i]);
        if(pmax[i]<=smin[i+1])rec[i]=1;
    }
    //nlogn
    int ans=0;
    forr(k,1,n){
        ans++;
        // cout<<k<<' ';
        for(int i=k;i<=n;i+=k){//这里的复杂度是logn
            if(rec[i]==0){
                ans--;
                // cout<<-1;
                break;
            }
        }
        // cout<<endl;
    }
    cout<<ans<<endl;
    // forr(i,1,n)cout<<pmax[i]<<' ';
    // cout<<endl;
    // forr(i,1,n)cout<<smin[i]<<' ';
    // cout<<endl;
}
/*
6
4 1 3 5 2 6  
2
*/

E 三维dp 滚动数组

const int N=510,K=1e3+10;
string mp[N];
//空间优化 滚动数组
//要从上面一行转移过来,存两行,奇数行1偶数行0
int dp[2][N][K];
void solve()
{
    int n,m,x;cin>>n>>m>>x;
    forr(i,0,m){
        forr(j,0,x){
            dp[0][i][j]=dp[1][i][j]=0;
        }
    }
    forr(i,1,n){
        cin>>mp[i];
        mp[i]='0'+mp[i];
    }
    forr(i,1,n){
        forr(j,1,m){
            forr(k,0,x){
                if(mp[i][j]=='1')dp[i&1][j][k]=max(dp[i&1][j-1][k],dp[(i-1)&1][j][k])+1;
                else if(mp[i][j]=='0') dp[i&1][j][k]=max(dp[i&1][j-1][k],dp[(i-1)&1][j][k]);
                else{//?
                    if(k>=1)dp[i&1][j][k]=max(dp[i&1][j-1][k-1],dp[(i-1)&1][j][k-1])+1;//?尽量变成1
                    else dp[i&1][j][0]=max(dp[i&1][j-1][0],dp[(i-1)&1][j][0]);//没有k=-1的状态 原地tp
                }
            }
        }
    }
    int ans=dp[n&1][m][0];
    forr(i,1,x){
        ans=max(ans,dp[n&1][m][i]);
    }cout<<ans<<endl;
}

F 排序

题意:让min和max尽量小。排序后,min是相邻数之间的值差,max是隔k-2个数的两值之差
vp时对怎么找k-1个值差的最小值有点犯难,枚举判断必超时,一开始想写纯双指针,但是维护最小值太麻烦,于是选择了傻瓜式的multi_set

void solve()
{
    int n,k;cin>>n>>k;
    vector<int>a(n+1),mind(n),maxd(n);
    forr(i,1,n)cin>>a[i];
    sort(a.begin()+1,a.end());
    forr(i,1,n-1){
        mind[i]=a[i+1]-a[i];
        // cout<<mind[i]<<' ';
    }//cout<<endl;
    int l=1,r=k-1;
    multiset<int>s;
    forr(i,1,k-1)s.insert(mind[i]);
    int ans=1e18+100;
    forr(i,1,n-k+1){
        int minn=(*s.begin());
        maxd[i]=a[i+k-1]-a[i];
        ans=min(maxd[i]*minn,ans);
        r++;
        if(r>=n)break;
        auto tp=s.lower_bound(mind[l]);//有序数组二分找要扔掉的值
        s.erase(tp);
        s.insert(mind[r]);
        l++;
    }
    cout<<ans<<endl;
}
/*
13 7
1 1 4 5 1 4 1 9 1 9 8 1 0

4 2
 114 514 1919 810

 6 3
 121 117 114 105 107 111
*/

H 贪心

题意:取的数个数相等,为k,让取的每个数四舍五入后尽量小/大

  • min:尽量四舍
    • 任何小数部分<0.5的数都可舍去,策略可以是每次分出 0.5 − δ 0.5-\delta 0.5δ,分k-1个
    • 剩下的数也要尽量小,取得 0.5 − δ 0.5-\delta 0.5δ尽量大,那么 δ → 0 , 1 e 9 ⋅ δ → 0 \delta\rightarrow 0,1e9\cdot\delta \rightarrow 0 δ0,1e9δ0,无穷小忽略,就是每次分出0.5,分k-1次剩下的取round
  • max:五入
    • 能五入的最小是0.5,让剩下的一个数尽量大,其他k-1个数就得取0.5
void solve()
{
    int n,k;cin>>n>>k;
    double rst1=1.0*n-(k-1)*0.5;
    int minn=round(rst1);
    minn=max(minn,0ll);
    double rst=1.0*n-(k-1)/2;
    int maxn=round(rst)+k-1;
    maxn=min(2*n,maxn);
    cout<<minn<<' '<<maxn<<endl;
}

K 构造

题意:形成一个环,环之间的差值是质数

  • 质数:2,3,5,7,11,13…大部分是奇数
  • 枚举得到n<=4,输出-1
  • 相邻奇/偶数之间的差是2,奇偶之间的差值是奇数,所以可以分奇偶进行构造
    belike(n为偶数) 1,3,5,7,…,n-3,n-1,n,n-2,6,4,2 但是2和n-1放错了位置,重新放
    2在5和7之间必然合法
    n-1在n-4和n-6之间必然合法(差是3,5)
    但是应该n-4!=7,并且n应该>6
    而且n-1!=7,让5和7位置固定
  • 枚举5~10的答案,更大的n分奇偶进行构造
void solve()
{
    //分奇偶
    int n;cin>>n;
    if(n<=4)return cout<<-1<<endl,void();
    switch (n)
    {
        case 5:
            cout<<"4 1 3 5 2"<<endl;break;
        case 6:
            cout<<"4 6 1 3 5 2"<<endl;break;
        case 7:
            cout<<"4 6 1 3 5 7 2"<<endl;break;
        case 8:
            cout<<"4 6 8 1 3 5 7 2"<<endl;break;
        case 9:
            cout<<"4 9 6 8 1 3 5 7 2"<<endl;break;
        case 10:
            cout<<"4 9 6 8 1 3 10 5 7 2"<<endl;break;
        case 11:
            cout<<"4 9 6 11 8 1 3 10 5 7 2"<<endl;break;
        default:{
            vector<int>ans;
            if(n&1){//奇数
                for(int i=1;i<=n-2;i+=2){
                    ans.push_back(i);
                    if(i==5)ans.push_back(2);
                    if(i==n-6)ans.push_back(n-1);
                }
                ans.push_back(n);
                for(int i=n-3;i>=4;i-=2){
                    ans.push_back(i);
                }
            }
            else{//偶数
                for(int i=4;i<=n-2;i+=2){
                    ans.push_back(i);
                    if(i==n-6)ans.push_back(n-1);
                }
                ans.push_back(n);
                for(int i=n-3;i>=1;i-=2){
                    ans.push_back(i);
                    if(i==7)ans.push_back(2);
                }
            }
            for(auto i:ans)cout<<i<<' ';
            cout<<endl;
        }
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到