2025“钉耙编程”中国大学生算法设计春季联赛(8)

按照难度顺序

1007 架子鼓

只要算出来击打两种鼓的时间,取并集即可。用pii维护分数,每次要把分数变成最简分数

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;

void solve(){
    int n,m;
    cin>>n>>m;
    
    int ans=1;
    set<pair<int,int>> s;

    int p=0,q=0;

    for(int i=1;i<=n;i++){
        int a,b;
        cin>>a>>b;

        if(p!=0 && q!=0){
            s.insert({p,q});
        }

        if(p==0 && q==0){
            p=a;
            q=b;
        }else{
            int u=a*q+p*b;
            int v=b*q;
            p=u/gcd(u,v);
            q=v/gcd(u,v);
        }
        
    }

    // for(auto [a,b]:s){
    //     cout<<a<<" "<<b<<endl;
    // }

    p=0,q=0;

    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;

        if(p!=0 && q!=0){
            if(s.count({p,q})) ans++;
            // cout<<p<<" "<<q<<endl;
        }

        if(p==0 && q==0){
            p=a;
            q=b;
        }else{
            int u=a*q+p*b;
            int v=b*q;
            p=u/gcd(u,v);
            q=v/gcd(u,v);
        }
        
    }

    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

1003 独到寒山顶

更是签到
多源BFS,把所有可能的起点先放进队列里,开始bfs,最后取重点的最小值即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;

void solve(){
    int n,m;
    cin>>n>>m;
    //n列m行

    vector<vector<int>> g(m+10,vector<int>(n+10,1)),dist(m+10,vector<int>(n+10,inf));

    for(int i=1;i<=n;i++){
        int r;
        cin>>r;
        while(r--){
            int idx;
            cin>>idx;
            g[idx][i]=0;
        }
    }

    queue<pii> q;

    for(int i=1;i<=n;i++){
        if(g[1][i]==1){
            q.push({1,i});
            dist[1][i]=1;
        }
    }

    while(q.size()){
        auto [x,y]=q.front();
        q.pop();

        int dx[]={-1,0,1,0};
        int dy[]={0,1,0,-1};

        for(int i=0;i<4;i++){
            int a=x+dx[i];
            int b=y+dy[i];

            if(a<1 || a>m || b<1 || b>n) continue;
            if(!g[a][b]) continue;
            if(dist[a][b]>dist[x][y]+1){
                dist[a][b]=dist[x][y]+1;
                q.push({a,b});
            }
        }
    }

    int ans=inf;

    for(int i=1;i<=n;i++){
        ans=min(ans,dist[m][i]);
    }

    cout<<ans<<endl;

}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

1001 拼图游戏

学习bitset的典题

从前往后,对每一行,二分的去找在哪一列是满足条件的,期间用bitset维护每一列的颜色,加一点前缀和的思想, 在纸上画一下很快就明白这个流程了

注意bitset的大小在编译期确定,所以需要开常量大小

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;

void solve(){
    int n,m,k;
    cin>>n>>m>>k;

    int g[n+10][m+10];

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>g[i][j];
        }
    }

    //对每一列开一个bitset,记录颜色
    vector<bitset<2001>> bits(m+10);
    
    //把涉及不到的都改成1
    for(int i=1;i<=m;i++){
        bits[i].set(0,1);
        for(int j=k+1;j<=2000;j++){
            bits[i].set(j,1);
        }
    
    }

    int ans=0;

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            bits[j][g[i][j]]=1;
            bits[j]|=bits[j-1];
        }

        int l=1,r=m;
        while(l<r){
            int mid=l+r>>1;
            if(bits[mid].all()) r=mid;
            else l=mid+1;
        }
        if(bits[l].all())ans+=(m-l+1);
    }
    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

1002 缓存系统

分组背包模板题。

因为对一组内,取第j个物品需要把这一组的前j个物品都取完,所以可以把组内第j个物品的看成,这一组内的前j个物品的总和。前缀和即可。就转换成了每组最多取一个的分组背包问题

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;

void solve(){
    int n,m,x;//组数,每组内的个数,背包容量
    cin>>n>>m>>x;
    int sum=0;

    vector<vector<int>> w(n+10,vector<int>(m+10));// 读取次数 权值
    vector<vector<int>> a(n+10,vector<int>(m+10));// 内存 代价
    vector<vector<int>> f(n+10,vector<int>(x+10));

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j]>>w[i][j];
            sum+=w[i][j];
        }
        for(int j=2;j<=m;j++){
            w[i][j]+=w[i][j-1];
            a[i][j]+=a[i][j-1];
        }
    }

    //分组背包
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k=0;k<=x;k++){
                // 不选i组第j个物品
                f[i][k]=max(f[i][k],f[i-1][k]);
            }
            for(int k=x;k>=a[i][j];k--){
                //选i组第j个物品
                f[i][k]=max(f[i-1][k-a[i][j]]+w[i][j],f[i][k]);
            }
        }
    }

    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=x;j++){
            ans=max(ans,f[i][j]);
        }
    }
    cout<<sum-ans<<endl;

}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

1005 咒语附魔

数据纯随机,直接暴力就行。因为01随机生成,所以在每个数的位置都有50%的可能跳出,所以复杂度一定不会很大。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
const ll inf=1e18;
const int mod =1e9+7;

void solve(){
    int n,m;
    cin>>n>>m;

    string s,t;
    cin>>s>>t;

    s=" "+s;
    t=" "+t;

    vector<vector<int>> pos(n+10);

    for(int i=1;i<=m-n+1;i++){
        int cnt=0;
        for(int j=i,k=1;j<=m && k<=n;j++,k++){
            if(t[j]!=s[k]) cnt++;
            else break;         
        }
        pos[cnt].push_back(i);    
    }

    vector<string> strs;

    for(int i=n+9;i>=0;i--){
        if(pos[i].size()){
            for(auto idx:pos[i]){
                string tmp;
                for(int i=1;i<=n;i++){
                    if(s[i]==t[i+idx-1]) tmp.push_back('0');
                    else tmp.push_back('1');
                }
                strs.push_back(tmp);
            }
            break;
        }
    }

    sort(strs.begin(),strs.end());
    
    if(strs.size()==0){
        cout<<'0'<<endl;
    }else{
        int ans=0;
        string str=strs.back();
        for(auto ch:str){
            if(ch=='1') ans++;
        }
        cout<<ans<<endl;
    }


}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}

1010 字符串哈希

好神奇的题目,每次做杭电都能对已有的知识点有新理解

因为B(s)的值范围很小,随意枚举这个值 i ,0-10006。

用i算出 i * i * i * c + i * i * d + i * e + f

算出这个值后,又因为A(s) 的哈希值不会重复,所以可以用这个值,去反推出来字符串。这个过程只需要把这个值当成一个27进制的数字即可。推出字符串,如果符合条件,则ans++。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
using pii=pair<int,int>;
using ll=long long;
using lll=__int128_t;
const ll inf=1e18;
const int mod =1e9+7;
const int M=10007;

void solve(){
    int k,c,d,e,f;
    cin>>k>>c>>d>>e>>f;

    vector<int> p10(12);
    p10[0]=1;

    for(int i=1;i<=10;i++){
        p10[i]=p10[i-1]*10%M;
    }

    int ans=0;

    for(int i=0;i<M;i++){
        //现在是在枚举bi,要根据bi的算出来哈希值,去反推A(s)的s,在验证A(s)是否等于B(s)
        lll val=i*i*i*c+i*i*d+i*e+f;
        vector<int> a;
        while(val){
            a.push_back((int)val%27);
            val/=27;
        }

        if(a.size()>k || a.size()==0) continue;

        int tmp=0;
        bool f=0;
        for(int j=0;j<a.size();j++){
            if(a[j]==0) f=1;
            tmp+=a[j]*p10[j];
            tmp%=M;
        }

        if(!f && tmp==i) ans++; 
    }

    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int ct=1;
    cin>>ct;

    while(ct--){
        solve();
    }
    return 0;
}
posted @ 2025-04-30 22:08  LYET  阅读(89)  评论(0)    收藏  举报