• 2017-8-31 NOIP模拟赛


    可接受序列

    【题目描述】

    让计算机这样读入一列非负整数:

    1、读入数T。                   

    2、接着往下读入T个数。

    3、如果数列读完了,则停止,否则,转到1。

    但是,往往会出现这样的问题:执行第2步时,数列已经没有T个数了。如果这样,我们称这个数列是“不可接受的”,否则,称它是“可接受的”。我们需要用最少的步数把一个数列变成“可接受的”,一步是指:

    1、把数列中的某一个数加1。

    2、把数列中的某一个数减1。

    【输入格式】

    第一行有一个数N (1<=N<=1000000),表示数列的长度,接下来有n行,描述这个数列,每一行有一个非负整数(不超过1000000)。

    【输出格式】

    仅一个数,表示最少的步数。

    【输入输出样例】

    sequence.in

    sequence.out

    7

    3

    1

    2

    3

    4

    5

    6

    1

    【数据规模】

    对于50%的数据,N≤1,000;

    对于80%的数据,N≤100,000;

    对于100%的数据,N≤1,000,000。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    using namespace std;
    int n,a[1000010];
    long long ans=9999999999;
    void dfs(int pos,long long cnt){
        if(cnt>=ans)return;
        //if(pos>n)return;
        if(pos==n+1){
            ans=min(ans,cnt);
            return;
        }
        if(pos==n){
            dfs(pos+1,cnt+a[pos]);
            return;
        }
        for(int i=0;i+pos<=n+1;i++){
            int step=abs(a[pos]-i);
            dfs(pos+i+1,cnt+step);
        }
    }
    int main(){
        //freopen("Cola.txt","r",stdin);
        freopen("sequence.in","r",stdin);
        freopen("sequence.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        dfs(1,0);
        cout<<ans;
        return 0;
    }
    20分 暴力深搜
    /*
        由于走到某个数字的最小操作数只与那个走到它的数字有关,所以问题满足无后效性,问题可以转变为问走到第n+1个位置的最小操作数。
        f[i]表示走到第i个数字的最小操作数,那么可以以它为中心来更新后面位置的最小操作数,答案为f[n+1]
    */
    #include<cstdio>
    const int maxn=1000010;
    int n;
    int s[maxn],f[maxn];
    int main(){
        freopen("sequence.in","r",stdin);
        freopen("sequence.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&s[i]);
            f[i]=maxn;
        }
        int a,b,c;
        f[1]=0,f[n+1]=maxn;
        for(int i=1;i<=n;i++){
            a=i+s[i]+1;
            b=f[i];
            if(a>n+1){
                c=a-n;
                for(int j=n+1;j>i&&f[j]>n-j+b+c;j--)f[j]=n-j+b+c;
            }
            else if(f[a]>b){
                f[a]=b;
                for(int j=a-1;j>i&&f[j]>a-j+b;j--)f[j]=a-j+b;
                for(int j=a+1;j<=n+1&&f[j]>j-a+b;j++)f[j]=j-a+b;
            }
        }
        printf("%d
    ",f[n+1]);
        return 0;
    }
    100分 dp

     

    促销

    【题目描述】

        某某商场搞了一个促销活动,促销活动以以下方式进行:

    1. 一个想要参加活动的顾客将他的发票扔进抽奖箱里。
    2. 在每天的最后,从抽奖箱里抽出两张发票:

    a)       金额最大的发票a

    b)       金额最小的发票b

    1. 金额最大的发票的持有者得到a-b的奖金。

    每天被抽出的发票都不会再被放回抽奖箱里。

    你想知道促销活动结束时一共付出了多少奖金。

    【输入格式】

        第一行一个N,促销进行的天数;

        接下来N行,第一个数是一个ki,表示第i天收到的发票数;接下来ki个数,每个数是一个发票的金额。

    【输出格式】

        一个数,整个促销活动过程中一共付出了多少奖金。

    【输入输出样例】

    promotion.in

    promotion.out

    5

    3 1 2 3

    2 1 1

    4 10 5 5 1

    0

    1 2

    19

    【数据规模】

        对于30%的数据,发票总数M不超过2000;

        对于另外20%的数据,每张发票的金额不超过2000;

        对于100%的数据,发票总数M不超过1000000,每张发票的金额不超过1000000。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    priority_queue<int>heap1;
    priority_queue<int>heap2;
    int n;
    long long ans;
    int main(){
        //freopen("Cola.txt","r",stdin);
        freopen("promotion.in","r",stdin);
        freopen("promotion.out","w",stdout);
        scanf("%d",&n);
        int x,y;
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            for(int j=1;j<=x;j++){
                scanf("%d",&y);
                heap1.push(y);
                heap2.push(-y);
            }
            int a,b,a1,b1;
            a=heap1.top();heap1.pop();
            b=heap2.top();heap2.pop();
            if(a!=-b){
                ans+=a+b;
                continue;
            }
            a1=heap1.top();heap1.pop();
            b1=heap2.top();heap2.pop();
            if(a+b1>b+a1){
                heap1.push(a1);
                heap2.push(b);
                ans+=a+b1;
                continue;
            }
            else {
                heap1.push(a);
                heap2.push(b1);
                ans+=a1+b;
                continue;
            }
        }
        cout<<ans;
        return 0;
    }
    100分 两个堆

     

    亲和数

    【题目描述】

    某一天,老徐看了一本趣味数学书,上面提到了亲和数:定义数对 (x,y) 为亲和数对当且仅仅当x、y为不同正整数,且x、y各自的所有非自身正因子之和等于另一个数。例如 (220,284) 和 (284,220) 都是亲和数对,因为:220的所有非自身正因子之和为:1 + 2 + 4 + 5 + 10 + 11 + 20 + 22 + 44 + 55 + 110 = 284。284的所有非自身正因子之和为:1 + 2 + 4 + 71 + 142 = 220

    数对 (x,y ) 跟 (y,x) 被认为是同一数对,所以我们只考虑 x<y 的情况。

    老徐对某个范围内的亲和数对的数量非常感兴趣,所以希望你能帮她编写一个程序计算给定范围内的亲和数对的数量。给定一个范围A到B,如果A≤ x ≤ B,则我们称 (x,y)在范围[A,B]内。

    【输入格式】

    从文件的第一行分别读入正整数A和B,其中A、B满足

         1 ≤ A ≤ B ≤ 108  且 B-A ≤ 105

    【输出格式】

    输出文件只有一行,就是[A,B]内亲和数对的数量

    【输入输出样例】

    amicable.in

    amicable.out

    200 1200

    2

    注:[200,1200] 内的数对只有两个,分别是(220,284)和(1184 1210)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int a,b,bit[10],len,q[10];
    bool pan(int num){
        int sum=1;
        for(int i=2;i*i<=num;i++){
            if(num%i==0&&i*i!=num)sum+=i+num/i;
            if(i*i==num)sum+=i;
        }
        if(sum<=num)return 0;
        for(int i=2;i*i<=sum;i++){
            if(sum%i==0&&i*i!=sum)num-=i+sum/i;
            if(i*i==sum)num-=i;
            if(num<0)return 0;
        }
        if(num==1)return 1;
    }
    int shu(int pos,int num,bool limit){
        if(pos==len+1){
            if(pan(num))return 1;
            return 0;
        }
        int end=limit?bit[pos]:9;
        int ans=0;
        for(int i=0;i<=end;i++){
            ans+=shu(pos+1,num*10+i,limit&&i==end);
        }
        return ans;
    }
    int dp(int x){
        memset(q,0,sizeof(q));
        memset(bit,0,sizeof(bit));
        len=0;
        while(x){
            q[++len]=x%10;
            x/=10;
        }
        for(int i=len,j=1;i>=1;i--,j++)bit[i]=q[j];
        return shu(1,0,1);
    }
    int main(){
        //freopen("Cola.txt","r",stdin);
        freopen("amicable.in","r",stdin);
        freopen("amicable.out","w",stdout);
        scanf("%d%d",&a,&b);
        cout<<dp(b)-dp(a-1);
        return 0;
    }
    50分 数位dp(记忆化没写成)
    /*
        对于一个给定正实数x,如果x≡0(mod i),那么x≡0(mod x/i)。
        这样一来我们可以将区间放缩,由[1,n]缩小到[1,√n],每次如果x≡0(mod i),SUM+=i+n/i;值得一说的是,区间到达√n的时候,需要进行特判。
    
        如果x≡0(mod √n),那么需要和只需要加一个√n就好了。
    */
    #include<cstdio>
    int a,b,ans;
    int div(int n){
        int i=2,m=1;
        for(;i*i<n;i++)if(n%i==0)m+=i+n/i;
        if(i*i==n)return m+i;
        return m;
    }
    int main (){
        scanf("%d %d",&a,&b);
        for(;a<=b;a++){
            int x=div(a);
            if(x>a&&div(x)==a)ans++;
        }
        printf("%d",ans);
        return 0;
    }
    100分
  • 相关阅读:
    2019-2020-1学期20192405《网络空间安全专业导论》第七周学习总结
    2019-2020-1学期20192402《网络空间安全专业导论》第十二周学习总结
    2019-2020-1学期20192402《网络空间安全专业导论》第十一周学习总结
    2019-2020-1学期20192402《网络空间安全专业导论》第十周学习总结
    2019-2020-1学期20192402第九周《网络空间安全专业导论》学习总结
    2019-2020-1学期20192402《网络空间安全专业导论》第八周自学总结
    2019-2020-1学期20192402《网络空间安全专业导论》第七周学习总结
    2019-2020-1学期20192402《网络空间安全专业导论》第七周学习总结
    2019-2020-1学期20192402《网络空间安全专业导论》第七周学习总结
    2019-2020-1学期20192402《网络空间安全专业导论》第六周自学总结
  • 原文地址:https://www.cnblogs.com/thmyl/p/7459681.html
Copyright © 2020-2023  润新知