开学训练第一周周报

  在这一周学习了新的算法,和新的思路,其中发现了自己的一些待改进的地方;

算法:二进制枚举、寻找质数线性筛法、(还有二进制的位运算,异或^,与&,或|,非~,以前对这些没有了解)

新思路:把问题拆分开;有许多的题从表面看都很难直接 ac,需要找到题的规律,再进行解答;

待改进的地方:对于c++的容器使用还不熟练,还有一些函数也不会用;在做题的时候会马虎掉重要的描述,有时还把一道题完全理解错了(这些问题证明还需要多刷题,有一句话叫见多识广嘛);

幸运号码

暂无标签

 题目讨论 题目统计 全部提交

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:中等
分数:25 OI排行榜得分:5(0.1*分数+2*难度)
出题人:smuoj

描述

NoonMaple对数字情有独钟,如果他发现了一个幸运号码,他就会感到快乐。

幸运号码是指符合以下条件的号码:

1、它的长度是偶数;

2、存在一个位置,将字符串从此位置分成左右两部分,它的左半部分的数字之和等于右半部分的数字之和;

例如:123455 是幸运数字。

现在有 n 个号码 S1​,S2​,S3​,......,Sn​。请问有多少对 (i,j) (1≤i,j≤n) 满足 Si​+Sj​ 是幸运号码?

注意:上面的 + 运算符表示连接两个字符串。例如 s1​=123,s2​=789,那么 s1​+s2​=123789。

等NoonMaple数完所有的幸运号码后,就可以去挑战黄金树了。

输入描述

第一行包含一个整数 n(1≤n≤2∗105) 表示号码数量。

第二行包含 n 个非空字符串 S1​,S2​,......Sn​,保证每个字符串的长度不超过 9 并且只有数字 1 到 9。

输出描述

输出一个整数,表示有多少对 (i,j)(1≤i,j≤n) 满足 si​+sj​ 是幸运号码。

用例输入 1 

10
5 93746 59 3746 593 746 5937 46 59374 6

用例输出 1 

20

用例输入 2 

5
2 22 222 2222 22222

用例输出 2 

13

用例输入 3 

3
1 1 1

用例输出 3 

9

解析:

首先用sum将每个字符串的每个数字和记录下来,并用anst[200][2]来记录数字出现的次数,[2]表示字符串的奇偶,用 与(&1)判断,为1就是就是奇数,否则为偶数;

然后将每个s作为左端,lsum为左半部分,找和lsum相等的右半部分( rsum+anst[lsum-reum][ s[i].size()&1])和他配对的就有anst[lsum-reum][ s[i].size()&1]个;作为sj右端同理;

代码:
#include<iostream>
using namespace std;

string s[200005];
int n,sum[200005];
int res=0;
int anst[200][2]={0};
int numm(string a){
    int num=0;
    for(int i=0;i<a.size();i++){
        num+=(a[i]-'0');
    }
    return num;
}
void solve(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>s[i];
        sum[i]=numm(s[i]);
        anst[sum[i]][s[i].size()&1]++;//anst[i][j],i 为每位和,j记录奇偶,其值为每位和y有多少个;
    }
    for(int i=0;i<n;i++){//light
        int lsum=0;
        for(int j=0;j<s[i].size();j++){
            int rsum=0;
            lsum+=s[i][j]-'0';
            rsum=sum[i]-lsum;
            if(lsum<rsum) continue;
            res+=anst[lsum-rsum][s[i].size()&1];
        }
    }
    for(int i=0;i<n;i++){//right
        int rsum=0;
        for(int j=s[i].size()-1;j>0;j--){//j不取等号因为在左端搜索时就已经用过全部的s了;
            int lsum=0;
            rsum+=s[i][j]-'0';
            lsum=sum[i]-rsum;
            if(rsum<lsum) continue;
            res+=anst[rsum-lsum][s[i].size()&1];
        }
    }
    cout<<res;
}
int main(){
    solve();
    return 0;
}

奶茶袋收集

暂无标签

 题目讨论 题目统计 全部提交

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:简单
分数:20 OI排行榜得分:2(0.1*分数+2*难度)
出题人:smuoj

描述

众所周知,ACM实验室的传奇学长Z学长非常爱喝奶茶,他非常喜欢收集各式各样的奶茶联名袋子,他目前已经拥有了 n 个不同的奶茶袋子。

在某一天,他将自己的 n 个袋子按照高度顺序从低到高放在了ACM集训队的窗台上,他希望将这些袋子分成 m 个连续段,使得所有分段的极差之和尽可能的小。

简单的来讲,你需要将一个长度为 n 的序列分为 m 段,我们定义 si​ 为第 i 个分段的极差,你需要找出最小的 ∑i=1m​si​。

极差:是指每个分段中最高和最矮的袋子高度之差,例如有一个分段为{1,2,7,10},那么极差为10−1=9。

分段:每一段在原始序列中是一段连续区间,例如将{1,2,3,4,5,6}分成两段,{1,2,3}{4,5,6} 是合法的,而类似于{1,2,4}{3,5,6}或{1,3}{4,5,6}是不合法的。

145658087d824d2c9347fd9d49e7dd80.jpg

输入描述

第一行输入两个整数 n,m(1≤m≤n≤105),代表袋子的数量和分段的数量。
第二行输入 n 个整数 a1​,a2​,...,an​,代表每个袋子的高度,其中1≤a1​≤a2​≤a3​≤...≤an​≤109。

解析:

需要找出使极差最小的值。先将相邻两数的极差算出来,要分为m段,每有m段就需要减去m-1个相邻极差,要求最小,只需要将最大的m-1个极差减去,就能得到最小极差;可用sort和vetor 容器实现;

代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> ca;
int n,m;
int a[100005];
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    for(int i=0;i<n-1;i++){
        ca.push_back(a[i+1]-a[i]);
    }
    sort(ca.begin(),ca.end());
    for(int i=1;i<=m-1;i++) ca.pop_back();
    int num=0;
    while(ca.size()){
        num+=ca.back();
        ca.pop_back();
    }
    cout<<num;
    return 0;
}

基于文化课的算法学习

暂无标签

 题目讨论 题目统计 全部提交

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:简单
分数:15 OI排行榜得分:2(0.1*分数+2*难度)
出题人:smuoj

描述

zxz特别喜欢学习算法,可是zxz天天被繁杂的课程实验压的喘不过气来,于是zxz决定偷偷抄别人的实验报告来为自己学习算法腾出时间,可是zxz正准备直接修改别人的名字时却发现对方在实验报告中引入的代码变量名就是他自己的名字,如果直接使用word文档的查找替换功能修改代码中的名字就会导致程序报错,你可以编写代码帮助zxz将代码外的名字修改成 “zxz” 吗?(一段代码的开头为实验报告的第一个 “main”,结尾为实验报告的最后一个 “return”)

替换按照下标越小,优先级越高的方法进行,例如要替换的姓名为 “aaa”,代码中能替换的一段连续字串为 “aaaaa” ,则这段字串会被替换为 “zxzaa”。

特殊的,如果要替换的姓名为 “zxx”,代码中能替换的一段连续字串为 “zxxxx”,则这段字串会被替换为 “zxzxz” 而不是 “zxzxx”。(出题人太善良了,本来不想给这个提示的)

当然如果zxz发现实验报告中的代码有误(即不包含 “main” 或 “return”)那么就输出 “wrong” 提醒zxz。

输入描述

第一行输入一个整型 n (1≤n≤105),表示实验报告内容的长度。

第二行输入一个字符串 A (∣A∣=3),表示zxz需要修改的人的名字。

第三行输入一个长度为 n 的字符串 S ,表示需要实验报告的内容。

输出描述

输出一行字符串,表示修改后的代码;若源代码有误,则输出 “wrong”。(不包括引号)

用例输入 1 

66
wly
sadadwfdgdswlyabcdefmainwlyymainshaniznixisfnreturnwlyssaiwlysnan

用例输出 1 

sadadwfdgdszxzabcdefmainwlyymainshaniznixisfnreturnzxzssaizxzsnan

用例输入 2 

16
abc
abcmianabcreturn

用例输出 2 

wrong

解析:

简单来说需要判断是否有main开头,和return 结尾,没有就wrong,有就记录第一个出现main的位置,和·最后一个出现return的位置,将除他们之外的需要更改的名字更改

代码:
#include<iostream>
using namespace std;
int n,x,y;
int m=1,r=1;
string a,s,ma="main",re="return",w="zxz";
void name(int i){
    int j;
    for(j=1;j<3;j++){
        if(s[i+j]!=a[j]) break;
    }
    if(j==3){
        for(int t=0;t<3;t++){
            s[i+t]=w[t];
        }
    }
}
void Main(int i){
   int j;
    for(j=1;j<4;j++){
        if(s[i+j]!=ma[j]) break;
    }
    if(j==4) x=i,m=0;
}
void Return(int i){
    int j;
    for(j=1;j<6;j++){
        if(s[i+j]!=re[j]) break;
    }
    if(j==6) y=i+j,r=0;
}
int main(){
    cin>>n;
    cin>>a;
    cin>>s;
    for(int i=0;i<n;i++){
        if(s[i]=='m'&&m==1){
            Main(i);
        }
        if(s[i]=='r'){
            Return(i);
        }
    }
    for(int i=0;i<x;i++){
        if(s[i]==a[0]){
            name(i);
        }
    }
    for(int i=y;i<n;i++){
        if(s[i]==a[0]){
            name(i);
        }
    }   
    if(r==1||m==1){
        cout<<"wrong";
    }
    else{
        cout<<s;
    }
    return 0;
}

B 孵化小鸡

暂无标签

 题目讨论 题目统计 全部提交

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:简单
出题人:PHarr

描述

在某个农场中正在孵化小鸡,每颗鸡蛋都放置在一个孵化小鸡的机器中,其中鸡蛋都排成一排我们可以用一个横轴来表示。存在N种不同的鸡蛋,每一种不同品质的鸡蛋都放置在同一片区域,且他们存在一个温暖值mi​只有鸡蛋位置上的温度达到所需的温暖值该鸡蛋才会孵化。现在你可以设置M个暖源,每个暖源可以使[li​,ri​]位置上的温度上升ki​,每个暖源的价格为pi​。
现在需要你使用最少的花费让所有鸡蛋都可以正常孵化。

输入描述

第一行输入俩个正整数 N,M;
接下来的N行中分别输入ai​,bi​,mi​ 分别表示每种鸡蛋分布于[ai​,bi​]它们的温暖值为mi​;
接下来的M行中分别输入li​,ri​,ki​,pi​ 分别表示每种暖源能使覆盖的区域[li​,ri​]温度上升ki​,其花费为pi​。
1≤N≤20
1≤M≤10
1≤ai​,bi​,li​,ri​≤100
1≤ki​,mi​≤106
1≤pi​≤1000

输出描述

输出一个数表示可以让所有鸡蛋都正常孵化的最少花费。(保证有解)

用例输入 1 

2 4
1 5 2
7 9 3
2 9 2 3
1 6 2 8
1 2 4 2
6 9 1 5

用例输出 1 

10

解析:

本题数据较少,使用二进制枚举,将每种暖源的方案可能性都枚举一下;最后记录合理的花费,遇到更小的花费更新;

代码:
#include<iostream>
#include<vector>
using namespace std;
int n,m,x,l,r,a[102],lm[11],rm[11],k[11],p[11];
void solve(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        cin>>l>>r>>x;
        for(int j=l;j<=r;j++){
            a[j]=x;
        }
    }
    for(int i=0;i<m;i++){
        cin>>lm[i]>>rm[i]>>k[i]>>p[i];
    }
    int res=1e6;
    for(int i=0;i<1<<m;i++){//有2的m次种方案;
        int b[102]={0};
        int  ans=0;
        for(int j=0;j<m;j++){
            if((i>>j)&1){//右移j,判断0,1,1为用了j号光源
                ans+=p[j];
                for(int t=lm[j];t<=rm[j];t++){
                    b[t]+=k[j];
                }
            }
        }
        int log=1;
        for(int i=1;i<=100;i++){
            if(a[i]-b[i]>0){//判断是否是合理的方案;
                log=0;
            }
        }
        if(log!=0){
            if(ans<res){//更新最小;
                res=ans;
            }
        }
    }
    cout<<res<<endl;
}
int main(){
    solve();
    return 0;
}

 

D 划分田地(easy)

暂无标签

 题目讨论 题目统计 全部提交

时间限制:C/C++ 1000MS,其他语言 2000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:简单
出题人:PHarr

描述

划分田地的两个版本仅数据范围上存在不同,请仔细读题。

在一个无限大的二维田地中种植了N颗土豆,第i颗土豆的坐标为(ai​,bi​),且保证每行每列至多一个土豆。现在需要在这片田地中划分出一片矩形实验地,该矩形的边严格平行与x轴与y轴。在矩形实验地中的土豆都将会成为实验土豆,请问一共多少种不同的实验土豆集合?空子集算方案数的其中之一。

输入描述

第一行输入一个正整数N;
接下来的N行分别输入两个非负整数ai​,bi​表示第i颗土豆的坐标。
1≤N≤10
0≤ai​,bi​≤50

输出描述

输出满足题目条件的方案数。

用例输入 1 

4
0 2
1 0
2 3
3 5

用例输出 1 

13

解析:

二进制枚举,需要不同的土豆实验地的集合,枚举每一种选法看是否合理,每选入的土豆不能在集合里面,否则就不合理;合理+1;

代码:

I 找除数

暂无标签

 题目讨论 题目统计 全部提交

时间限制:C/C++ 2000MS,其他语言 4000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:简单
出题人:PHarr

描述

已知一个正整数n,请问有多少个不同的数x能满足n取余x等于0。

输入描述

本题为t组输入,在每一组数据中输入一个正整数n。
1≤t≤105;
1≤n≤108。

输出描述

每组数据用换行隔开,每行输出一个数表示对应的答案。

I 找除数

暂无标签

 题目讨论 题目统计 全部提交

时间限制:C/C++ 2000MS,其他语言 4000MS
内存限制:C/C++ 256MB,其他语言 512MB
难度:简单
出题人:PHarr

描述

已知一个正整数n,请问有多少个不同的数x能满足n取余x等于0。

输入描述

本题为t组输入,在每一组数据中输入一个正整数n。
1≤t≤105;
1≤n≤108。

输出描述

每组数据用换行隔开,每行输出一个数表示对应的答案。

解析:

本题涉及质因数分解,如18的除数有1 2 3  6 9 18;可分解为一个2*3的二次2^1,3^2;答案就为1*(1+1)*(2+1)1,2分别为2,3的次数;通过这个规律可以发现其除数为其质数次方加1相乘;

代码:
#include<iostream>
using namespace std;

int main(){
    int t,n;
    cin>>t;
    while(t--){
        cin>>n;
        int ant=1;
        for(int i=2;i*i<=x;i++){    
            if(n%i!=0) continue;
            int ans=0;
            while(n%i==0){//判断为i几次;
                n=n/i;
                ans++;
            }
            ant*=(ans+1);
        }
        if(n>1){
            ant*=2;
        }
        cout<<ant<<endl;
    }
    return 0;
}

本周收获还是不小的,希望能一直坚持下去

  • 36
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值