ACM寒假集训第一讲作业

LONG LOONG

思路

一定会先打印一个'L'中间输出若干个‘o’,然后必然结尾是"ng",因此只需要中间用循环,然后头和尾写固定的输出即可。

代码

#include<iostream>
using namespace std;
int main(){
    int N;
    cin>>N;
    cout<<'L';
    for(int i=0;i<N;++i) cout<<'o';
    cout<<"ng";
    return 0;
}

YES OR YES?

思路

方法一,将输入的字符串转换为全部大写或者全部小写,然后跟"YES"或者"yes"进行比较
方法二将给的字符串和所有可能的结果进行比较,如果都不想等就输出"N"

代码

#include<iostream>
using namespace std;
void judge(){
    string str,pre_str="YES";
    bool flog=true;
    cin>>str;
    for(int i=0;i<3;++i){
        if(str[i]!=pre_str[i]&&str[i]!=pre_str[i]-'A'+'a'){
            flog=false;break;
        }
    }
    if(flog) printf("YES\n");
    else printf("NO\n");
}
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;++i){
        judge();
    }
    return 0;
}

Even? Odd? G

思路

根据题意可知,计算机中没有任何一种整数类型可以表示长度为60的数字,所以考虑用字符串来读取这个数值,然后根据偶数的特点:最后一位数字是偶数来解决这个问题

代码

#include<iostream>
using namespace std;

int main(){
    string str;
    int n;
    cin>> n;
    for(int i=1;i<=n;++i){
        cin>>str;
        int len=str.length();
        if(str[len-1]=='2'||
            str[len-1]=='4'||
            str[len-1]=='6'||
            str[len-1]=='8'||
            str[len-1]=='0') cout<<"even"<<endl;
        else cout<<"odd"<<endl;
    }
    return 0;
}

Problem Generator

思路

既然要得到至少需要生成几题那就需要知道有哪些是已经满足题意的,因此可以设置一个长度为 7 的一维数组来记录,将其初始化为 0 ,然后读入已有的题的字符串,每读入一个字母找到对应的格子,如果这个格子存储的数值大小已经等于了比赛的论述,读取到这个等级的题目也不+1,而是直接continue
读取完后再遍历一遍数组,看哪个格子中的值小于比赛轮数,就计算这个等级题目差多少道,加到计数器中

代码

#include<iostream>
using namespace std;
int main(){
    int num;
    cin>>num;
    for(int i=1;i<=num;++i){
        short flog[10];
        for(int j=0;j<10;++j) flog[j]=0;
        int n,m,cnt;
        string a;
        cin>>n>>m;
        cnt=7*m;
        cin>>a;
        for(int j=0;j<n;++j){
            if(flog[a[j]-'A']<m) {
                ++flog[a[j]-'A'];
                --cnt;
            }
        }
        cout<<cnt<<endl;
    }
    return 0;
}

rules

思路

为了解决这个问题,我需要统计每天有多少人遵守了k规则,一共有多少天k规则是符合民意的

代码

#include<iostream>
using namespace std;

int main(){
    int n,m,k,cnt_m=0,cnt_n=0,temp_r;
    cin>>n>>m>>k;//n居民数,m天数,k规则
    for(int i=0;i<m;++i){
        cnt_n=0;
        for(int j=0;j<n;++j){
            cin>>temp_r;
            if(temp_r==k) ++cnt_n;
        }
        if(cnt_n>=(n+1)/2) ++cnt_m;//注意这里人数的一半是要向上取整
    }
    if(cnt_m>=(m+1)/2) cout<<"YES"<<endl;//这里m的一半也要向上取整
    else cout<<"NO"<<endl;
    return 0;
}

ManyRepalcement

思路

本题要求对字符串进行操作。将字符串中所有指定的字母全部替换为另一个字母
传统方法是依次遍历这个字符串,当遇到需要被替换的字母c就将其更改为d
这种方法的时间复杂度是O(N*Q)
为了能更高效的更改,我想到的是能够尽快的将所有位置的信息一步更新,而不是遍历这个字符串来更新。如果能将同一字母的不同位置同时修改为指定的字母就可以大大提高更改的效率
因此我通过并查集,将同一字母不同位置分布视为等效类,并且通过两个一维数组分别记录字母d对应等效类,和某一个等效类对应哪一个字母。

代码

#include<iostream>
using namespace std;
class uset{
    public:
        uset(int size):max_size(size+1){
            root=new int[max_size];
            parent=new int[max_size];
            for(int i=0;i<=size;++i) root[i]=parent[i]=1;
        }
        int uhead(int e){//找到祖宗是哪一个
            if(1==root[e]) return e;
            parent[e]=uhead(parent[e]);
            return parent[e];
        }
        int umerge(int i,int j){
            i=uhead(i),j=uhead(j);
            if(i==j) return i;
            root[i]=0;
            parent[j]+=parent[i];
            parent[i]=j;
            return j;
        }
    public:
        int*root,*parent;
        int max_size;
};
const int MAX=200001;
int ci_map[26];//下标代表字母,内容代表这个字母对应的等效类的uhead值
char ic_map[MAX];//某个数字对应的字母是哪个,如果数值范围会很大就会用map
int main(){
    int N,Q;
    char ch;
    char c,d;
    string s;
    cin>>N>>s>>Q;
    uset us(N);
    for(int i=1;i<=N;++i){
        if(ci_map[s[i-1]-'a']){
            us.umerge(i,ci_map[s[i-1]-'a']);
        }else{
            ic_map[i]=s[i-1];
            ci_map[s[i-1]-'a']=i;
        }
    }
    for(int i=1;i<=Q;++i){
        cin>>c>>d;
        if(c==d||0==ci_map[c-'a']) continue;
        if(ci_map[d-'a']){
            us.umerge(ci_map[c-'a'],ci_map[d-'a']);//将下标类合并为一个等效类
            //关于类“c"的数据都擦除
            ic_map[ci_map[c-'a']]=0;
            ci_map[c-'a']=0;
        }else{
            //没有类d就将类c改名为类d
            int x=ci_map[c-'a'];
            ci_map[c-'a']=0;
            ic_map[x]=d;
            ci_map[d-'a']=x;
        }
    }
    for(int i=1;i<=N;++i){
        cout<<ic_map[us.uhead(i)];
    }
    return 0;
}

更好的交换

思路

问题最后希望通过有限的整行整列操作得到一个新的矩阵,而这个新的矩阵也只是数字的顺序不一样。
进行行变换不会影响到每一列有哪些数字,同样进行列变换不会影响行中有哪些数字,只是可能会影响某一行中数字出现的顺序。
既然行变换不会影响到每一列有哪些问题,列变换不会影响到每一行有哪些元素,那么就可以给每一列、每一行编号,一个行编号与一列编号就可以确定唯一的一个数值,则可以用这些编号代表某一行或者某一列。然后只交换这些编号,最终通过这些编号来看需要先输出哪一行哪一列。最终得到目标矩阵

代码

#include<iostream>
using namespace std;

int main(){
    int n,m,op,x,y;
    cin>>n>>m;
    int arr[n][n];
    int l[n],c[n];
    for(int i=0;i<n;++i){
        l[i]=c[i]=i;
        for(int j=0;j<n;++j){
            cin>>arr[i][j];
        }
    }
    for(int i=0;i<m;++i){
        cin>>op>>x>>y;
        if(op==0){//交换列
            swap(l[x-1],l[y-1]);
        }else if(op==1){//交换行
            swap(c[x-1],c[y-1]);
        }
    }
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j){
            cout<<arr[c[i]][l[j]];
            if(j!=n-1) cout<<' ';
        }cout<<endl;
    }
    return 0;
}

学习总结

通过本次专题课的学习,我学习到了更多书写c++程序的技巧,复习了数据结构中的STL中的容器,更是进一步理清了我的做题思路。

书写rules题目时,我一开始简单的模拟题目的意思,不仅记录了k规则是否符合民意,还记录了其他规则被遵守的情况,但实际上那些数据其实并不需要被记录,记录那些反而浪费时间浪费空间。我只需把目光放到k规则的遵守情况即可。

但有些问题不是简单的模拟题目的意思就可以解决的。如果只是简单的模拟题目,很可能不能在规定的时间内将任务解决。因此就需要更高效的解决问题,要回过头来看这个问题,究竟哪些时有必要的,哪些问题是等效的,哪些操作是必须要做的。像本次练习的hard部分,其实都不要清楚的知道中间过程字符串究竟是怎样的或者矩阵是怎样的,只需要知道字母c最终被替换成了哪一个字母d,或者只需要知道最终矩阵的某一行最终究竟落在了哪里就可以凭借此得出答案。

posted @ 2025-01-23 15:58  Buy-iPhone  阅读(11)  评论(0)    收藏  举报