周末总结4

这周做题也不多,没有一周我自己是满意的,没有那种知识的充实感,感觉是被任务推着走,计划好周末两天刷题加整理晚上写作业,但真的不如提前去做好。周日临时加了两篇思想汇报,一篇申情书,就很无语,希望接下来这周我再高效一点。下面我来整理一下这周做的一些基础题,不瞒大家说这些题整理完我还收获挺多的太菜啦

博客的题目看起来多,但都是一些容易理解的简单题,一些思路和实现相对比较重要些

字符串操作

P5015 [NOIP2018 普及组] 标题统计

题目
1.getline(cin,x)的使用

题意:计算除空格和换行符以外的字符数
输入:
234
输出:
3
输入:
Ca 45
输出:
4

#include <bits/stdc++.h>
using namespace std;
string s;
int main()
{
    getline(cin,s);
    int l=s.length();
    int ans=l;
    for(int i=0;i<l;i++)
    {
        if(s[i]==' ')
        {
            ans--;
        }
    }
    cout<<ans<<endl;
    return 0;
}

P1055 [NOIP2008 普及组] ISBN 号码(字符和整型转化)

题目

1.注意整型和字符的转化
2.学习优化后的代码实现方式:将余数放在数组中,利用了下标和余数相等

计算规则:见题

输入:0-670-82162-4
输出:Right
输入:0-670-82162-0
输出:0-670-82162-4

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    int j=1;
    int sum=0;
    int num=1;
    int ans=0;
    for(int i=0; i<=12; i++)
    {
        if(i==11)
        {
            ans=s[12]-'0';
            break;
        }
        if(s[i]!='-')
        {
            sum+=j*int(s[i]-'0');
            j++;
        }
    }
    sum=sum%11;
    if(sum==ans||(sum==10&&s[12]=='X')) cout<<"Right"<<endl;
    else
    {
        for(int i=0; i<=11; i++)
        {
            cout<<s[i];
        }
        if(sum==10) cout<<'X'<<endl;
        else   cout<<char('0'+sum)<<endl;
    }
    return 0;
}

优化:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    char a[14];
    char mod[12]="0123456789X";
    cin>>a;//输入字符串
    int i,j=1,sum=0;
    for(i=0; i<12; i++)
    {
        if(a[i]=='-') continue;
        sum+=(a[i]-'0')*j++;
    }
    if(mod[sum%11]==a[12]) cout<<"Right"<<endl;
    else
    {
        a[12]=mod[sum%11];
        cout<<a<<endl;
    }
  //  cout<<endl;
return 0;
}

P1308 NOIP2011 普及组 统计单词数(字符串函数的运用)

题目

C++ string 的使用
1.字符匹配:b.find(a)!=string::npos
2.转化大小写: b[i]=tolower(b[i]);

(详细使用见代码)

题意:
输入一个单词,和一行文章,输出出现该单词的个数和第一次出现的位置
没有该单词则输出 -1
分析:首先都将其转化为小写;查找有过没找到返回-1,如果找到了返回保留初始下标,并继续遍历计算总个数;(主要是函数的运用)
输入:
To
to be or not to be is a question
输出:
2 0

输入:
to
Did the Ottoman Empire lose its power at that time
输出:
-1

虽然这个代码还没过,40 scores
但我不打算晚上调它了(我就是个小拉吉)

#include <bits/stdc++.h>
using namespace std;
//知识点:
//字符匹配
//转化大小写
int main()
{
    string a,b;
    getline(cin,a);
    getline(cin,b);
    a=' '+a+' ';
    b=' '+b+' ';
    for(int i=0;i<b.length();i++)
    {
        a[i]=tolower(a[i]);
    }
    for(int i=0;i<b.length();i++)
    {
        b[i]=tolower(b[i]);
    }
    //转化成小写
    //寻找字符串a是否包含子串b
    //string::npos用来表示不存在的位置
    if(b.find(a)==string::npos)
    {
        cout<<-1<<endl;
    }//没有找到
    else
    {
        int chushi=b.find(a);//初始下标
        int n=b.find(a),sum=0;
        while(n!=string::npos)
        {
            ++sum;
            n=b.find(a,n+1);//从下一个位置开始查找
        }
        cout<<sum<<" "<<chushi<<endl;
    }
return 0;
}

P2010 [NOIP2016 普及组] 回文日期(思路)

题目

一个优化的题解肯定是思考了很久的,所以不必抱怨,还是再多练一点,积累一些思路
题意:
找两个日期间的回文串
分析:
直接简单粗暴遍历判断肯定超时,简直太狠啦,
该题解的思路:就是利用回文串前后串的对称,计算出十二个月对应的所有的回文串(因为月份和日期都已经确定所以回文串也将是唯一的),之后判断算出的回文串是否在该题目给定的两个年份之间的。

#include <iostream>

using namespace std;

int s[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};
//0229  倒过来9220 是闰年
int main()
{
    int n,m,c,sum=0;
    int ans=0;
    cin>>n>>m;
    for(int i=0;i<=12;i++)//枚举月和日
    {
        for(int j=1;j<=s[i];j++)
        {
            c=(j%10)*1000+
            (j/10*100)+
            (i%10*10)+
            (i/10);//算出前四位
            sum=c*10000+i*100+j;//算出整个日期
            if(sum<n||sum>m) continue;
            ans++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

P1012 [NOIP1998 提高组] 拼数(进阶比较大小)

题目
题意:
拼数,组成最大的数多多少少有点不会
分析:
一开始想到,拼数先比较最高位再比较较低位置,然而没有想到,其中一个截止了将比较它和另一数的最高位,看他们谁可以放在前面,举个例子理解这种情况32要优先321要靠前放,因为32321>32132,但我们一般常见的字符串比较会认为321>32的,所以将这种情况要考虑哟~

低级一点俗称详细一点的代码:

#include <iostream>
#define ll long long
using namespace std;
int t;
const int N=1e9;
bool cmp(int x,int y)
{
    return x>y;
}
//这个我在考虑存储数字的问题
//然而
string a[30];
//使用string存储更方便(可以直接比较大小)
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    for(int i=0;i<n;i++)//排序
    {
        for(int j=i+1;j<=n;j++)
        {
            if(a[j]+a[i]>a[i]+a[j])
            {
                swap(a[j],a[i]);
                /*
                举个例子 12,34
                1234<3412
                12和34 需要交换一下
                */
            }
        }
    }//排序结束
    //因为数据的个数少所以可以尝试着写双重循环,熟悉一下原理
    for(int i=0;i<=n;i++)
    {
        cout<<a[i];
    }
    return 0;
}

注意比较函数cmp()的定义,巧妙一些:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int t;
const int N=1e9;
bool cmp(string a,string b)
{
    return a+b>b+a;
}
//如果 return a>b
//会出现 321>32 的情况
string a[30];
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    sort(a,a+n,cmp);
    for(int i=0;i<n;i++)
    {
        cout<<a[i];
    }
    return 0;
}

P5587 打字练习(字符删除,精度四舍五入)

题目

中文题目,题意也很好理解,就不再赘述

分析:
1.捕获n行文章,用getline(cin,s),并计数
2.不要忘记原文中还要考虑‘<’这个符号,就离谱
3.删除是的计数问题,考虑到‘<’这个符号在开头,就删除一个它本身,如果不在开头(开始的第一的位置),这种情况j-1>=0,就删除它本身和前一个元素,因为最后输入的要和原文比较嘛,所以有个退格"<"就将前一个元素删除。
4.精确度的处理,遇到这种要求时 《请你计算出他的 KPM(Keys per minutes,每分钟输入的字符个数),答案四舍五入保留整数部分。》

看下面:

    //精确度的处理
    double k=sum;
    k=k*60/t;
    sum=k;//sum是int类型
    if(k-sum>0.5) sum++;//进一
    cout<<sum<<endl;
    return 0;

完整dama:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e3+10;

int main()
{
    string s[N];
    string ss[N];

    int ii=0,jj=0;
    getline(cin,(s[ii]));
    while(s[ii]!="EOF")
    {
        ii++;
        getline(cin,(s[ii]));
    }
    getline(cin,(ss[jj]));
    while(ss[jj]!="EOF")
    {
        jj++;
        getline(cin,(ss[jj]));
    }
    int t;
    cin>>t;
    int sum=0;
    for(int i=0; i<ii; i++)
    {
        for(int j=0; j<s[i].length(); j++)
            if(s[i][j]=='<')
                if(j-1>=0)
                {
                    s[i].erase(j,1);
                    s[i].erase(j-1,1),j-=2;
                }
                else  s[i].erase(j,1),j--;
    }
    for(int i=0; i<ii; i++)
    {
        for(int j=0; j<ss[i].length(); j++)
            if(ss[i][j]=='<')
                if(j-1>=0)
                {
                    ss[i].erase(j,1);
                    ss[i].erase(j-1,1),j-=2;
                }
                else  ss[i].erase(j,1),j--;
    }
//    for(int i=0; i<ii; i++)
//    {
//        cout<<ss[i]<<endl;
//    }
    for(int i=0;i<min(ii,jj);i++)
    {
        for(int j=0;j<min(s[i].length(),ss[i].length());j++)
        {
            if(s[i][j]==ss[i][j])
                sum++;
        }
    }
    //精确度的处理,没有精度处理只有10分
    double k=sum;
    k=k*60/t;
    sum=k;
    if(k-sum>0.5) sum++;
    cout<<sum<<endl;
    return 0;
}

函数,递归及递推

P1028 [NOIP2001 普及组] 数的计算(规律递推)

题目
题意:
给一个数n,在该数前面加一个不超过最前面那个数字的一半的数,直到不能加任何数,问有多少个满足该题意的数字。
6的话,是6,16,26,126,36,136六种,输出6
分析:
写一写找规律,简单的说一下,6的种数f[6]是1的种数f[1]+2的种数f[2]+3的种数f[3]

#include <bits/stdc++.h>
using namespace std;
const int N=1000;

int f[N];;
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=i/2;j++)
        {
            f[i]=f[i]+f[j];
        }
        f[i]++;
    }
//    for(int i=1;i<=n;i++)
//    {
//        cout<<f[i]<<" ";
//    }
    cout<<f[n]<<endl;
    return 0;
}

P1036 [NOIP2002 普及组] 选数(递归dfs)

題目

题意:
在n个数中选k个数相加和为素数的情况,输出有几种情况
分析:
重点是dfs()函数。可看代码详解,对于递归不熟可以纸上模拟一次
一次不行就两次

#include <bits/stdc++.h>
using namespace std;
bool prime(int n)
{
    for(int i=2;i*i<=n;i++)
        if(n%i==0)
            return false;
        return true;
}
int n,k;
long long ans;
int a[25];
//start可以保正是升序,因為排列中可能會存在重複,这样一来就去重啦
void dfs(int m,int sum,int start)//我们为了在n个数中找k个数字相加
{
    if(m==k)//k个数达到了要求,如果是素数的话就计数不是素数就return结束
    {
        if(prime(sum))
            ans++;
        return ;
    }
    for(int i=start;i<n;i++)
    {
        dfs(m+1,sum+a[i],i+1);//i+1是保证不重复之前的数
    }
    return ;
}
int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    dfs(0,0,0);
    cout<<ans<<endl;
    return 0;
}

P1464 Function(TLE,记忆化搜索)

题目
刚开始直接模拟,但TLE,需要用记忆化搜索
记忆化搜索:算法上依然是搜索,但搜索到的一些解用动态规划的那种思想和模式做一些保存。
一般来说,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态。
记忆化搜索,就是在求解的时候按照自顶向下的顺序,但是每次求解一个状态,就将它的解保存下来,以后遇到就不必重新求解。

该题也就是说,把每次0~20以内的答案记录下来,下一次递归时若有记录就直接输出就行,能省去大量的时间;这个题还要注意负数不能做下标,因此加一个特判就行。

爆啦

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll w(ll a,ll b,ll c)
{
    if(a<=0||b<=0||c<=0) return 1;
    else if(a>20||b>20||c>20) return w(20,20,20);
    else if(a<b&&b<c) return w(a,b,c-1)+w(a,b-1,c-1)+w(a,b-1,c);
    else return w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
}
int main()
{
    ll a,b,c,ans;
    while(cin>>a>>b>>c)
    {
        if(a==-1&&b==-1&&c==-1) break;
        ans=w(a,b,c);
    cout<<"w("<<a<<","<<b<<","<<c<<")="<<ans<<endl;
    }
    return 0;
}

AC

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll h[40][40][40];
ll w(ll a,ll b,ll c)
{
    if(a<=20&&b<=20&&c<=20&&a>=0&&b>=0&&c>=0)if(h[a][b][c]) return h[a][b][c];
    if(a<=0||b<=0||c<=0) return 1;
    if(a>20||b>20||c>20) return w(20,20,20);
    if(a<b&&b<c) return h[a][b][c]=w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
    return h[a][b][c]=w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
}
int main()
{
    ll a,b,c,ans;
    while(cin>>a>>b>>c)
    {
        if(a==-1&&b==-1&&c==-1) break;
    //printf("w(%lld, %lld, %lld) = %lld\n",a,b,c,w(a,b,c));
    cout<<"w("<<a<<", "<<b<<", "<<c<<") = "<<w(a,b,c)<<endl;
    }
    return 0;
}

P5534 【XR-3】等差数列(数学)

题意
这个就不说了

P1192 台阶问题(dp,找规律)

题目
方法一:
找规律:将k=1,2,3,列出来
k=2 : 1 2 3 5 8 13 21 34…
k=3 : 1 2 4 7 13 24 44 81…
k=4 : 1 2 4 8 15 29 56 108…
k=5 : 1 2 4 8 16 31 61 120…
自然会发现发现k=2时前两项是1,2; k=3时前三项是1,2,4;以此类推,发现k=n的话,前n项就是等差数列。其中公差是2;

当n<=k时,第N项=(上一项 x 2)%100003;
当n>k时 ,第N项=(上一项 x 2-第n-1-k项)%100003;

#include <bits/stdc++.h>
using namespace std;
#define ll long long
//有两种方法第一种是简单的找规律
//第二种Dp计算
const int N=1e5+10;
const int mod=100003;
int a[N];
int main()
{
    int n,k;
    cin>>n>>k;
    a[0]=1;
    a[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(i<=k)
        {
           a[i]=(a[i-1]*2)%mod;
        }
        else{
            a[i]=(a[i-1]*2-a[i-1-k])%mod;
        }
    }
    cout<<(a[n]+mod)%mod<<endl;//防止有负数
    return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define ll long long
//有两种方法第一种是简单的找规律
//第二种Dp计算
const int N=1e5+10;
const int mod=100003;
int d[N];
int main()
{
    int n,k;
    cin>>n>>k;
    d[0]=1;
    d[1]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=k;j++)
        {
            if(i>=j)
            d[i]=(d[i-j]+d[i])%mod;
        }
    }
    cout<<(d[n]+mod)%mod<<endl;
    return 0;
}

P1025 [NOIP2001 提高组] 数的划分(dp,递归)

我喜欢这个题
很经典的一个题,一个dp,一个递归

#include <iostream>

using namespace std;

int dp[205][10];
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1; i<=n; i++) dp[i][1]=1;
    for(int i=1; i<=n; i++)
        for(int j=2; j<=k; j++)
        {
            //else if(i==j) dp[i][j]=1;
            if(i>=j) dp[i][j]=dp[i-1][j-1]+dp[i-j][j];
            else dp[i][j]=0;
        }
       // for(int i=1; i<=n; i++)
       //for(int j=1; j<=k; j++)
       //  cout<<dp[i][j]<<" ";
          cout<<dp[n][k]<<endl;
    return 0;
}
#include <iostream>

using namespace std;

//搜索无非也是个递归的过程,纯纯学习别人家的代码

int n,k;
int dfs(int n,int p,int ii)//分别代表着待分配的数,分的部分数和当下要选出的数
{
    if(p==1) return 1;//直到p==1,退出递归
    int sum=0;//仔细想想为什么一定在函数内初始化呢,因为这里是递归呀,有return,其实已经保留了
    for(int i=ii;i<=n/p;i++)//剪枝,因为枚举到n会有重复的情况
    {
        sum+=dfs(n-i,p-1,i);//这个很容易懂
    }
    return sum;
}
int main()
{
    cin>>n>>k;
    cout<<dfs(n,k,1)<<endl;
    return 0;
}

P4994 终于结束的起点(滚动数组)

题目
题意很简单,斐波那契额数列取模的问题

因为f(n-1)mod M 和 f (n - 2 ) mod M最多只有 M2种取值,所以在M2次计算后一定出现过循环。最终在模M的斐波那契额数列数列都会是0,1,……,0,1……

不知道这个题还可以用什么数学方法推

但是可以用递推来模拟一下,可以用滚动数组存储对内存优化

f[2] = f[0];//作为辅助变量,起辅助作用
f[0] = f[1];//记录当前斐波那契额数列的值
f[1] = (f[2] + f[1]) % mod;//记录下一个 
#include<bits/stdc++.h>
using namespace std;
int f[5];
int main()
{
	int mod;
	cin>>mod;
	f[0] = 0;
	f[1] = 1;
	f[2] = 1;//初始化
	for(int i=1;i;i++){
		f[2] = f[0];
		f[0] = f[1];
		f[1] = (f[2] + f[1]) % mod;//滚存更新
		if((f[0]%mod==0)&&(f[1]%mod==1)) {cout<<i;return 0;}
		}
		return 0;
}

(周末总结整理又一次被我硬生生写成题解博客 xi~ )

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值