0607枚举算法试题

6.7枚举试题

测试说明

4个题目,3.5小时

以姓名命名文件夹,下存4个文件,分别命名。

1、洛谷P1865 A % B Problem

题目背景

题目名称是吸引你点进来的 实际上该题还是很水的

题目描述

区间质数个数

输入输出格式

输入格式:

一行两个整数 询问次数n,范围m

接下来n行,每行两个整数 l,r 表示区间

输出格式:

对于每次询问输出个数 t,如l或r∉[1,m]输出 Crossing the line

输入输出样例

输入样例#1:

2 5
1 3
2 6

输出样例#1:

2
Crossing the line

说明

【数据范围和约定】

对于20%的数据 1<=n<=10 1<=m<=10

对于100%的数据 1<=n<=1000 1<=m<=1000000 -10^9<=l<=r<=10^9 1<=t<=1000000

主要算法及思路:前缀和+筛法求素数

/*
1、对于100%的数据 1<=n<=1000 1<=m<=1000000 -10^9<=l<=r<=10^9 1<=t<=1000000
决定了我们必须用筛法求素数,才不可能TLE 
2、题目明确给出 if(l<1||r>m)  printf("Crossing the line
");(注意换行符)
3、不超界情况下,结果输出 尽量用前缀和计算,因为不用前缀和 point14会TLE 
我的tag[i]表示前i个数中素数的个数
4、综合评价:水!
(不过第一次朴素的ans++<俗称 一个一个数>,就被坑了,得了 93分) 
*/
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
#define N 1001000
int check[N]={1,1,0};
int tag[N];
int n,m,ans,tot;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=2;i<=m;i++){//筛法求素数 
        if(!check[i]){
            for(int j=2;i*j<=m;j++){
                check[i*j]=1;
            }
        }
    }
    for(int i=2;i<=m;i++){//前缀和 
        if(!check[i]) tag[i]=tag[i-1]+1;
        else tag[i]=tag[i-1];
    }
    for(int i=1,l,r;i<=n;i++){
        scanf("%d%d",&l,&r);
        if(l>r) swap(l,r);
        if(l<1||r>m){//判断是否出界 
            printf("Crossing the line
");
            continue;
        }
        printf("%d
",tag[r]-tag[l-1]);
    }    
    return 0;
}

2、codevs   2669 简单的试炼

题目描述 Description

已知一个数S,求X和Y,使得2^X+3^Y=S.

输入描述 Input Description

(多组数据)

每行一个整数S,当S=0时输入结束.

输出描述 Output Description

X和Y,以2^X+3^Y=S的形式输出,若有多组解,输出X最小的那组.

样例输入 Sample Input

13

33

0

样例输出 Sample Output

2^2+3^2=13

2^5+3^0=33

数据范围及提示 Data Size & Hint

对于30%的数据  S≤50,000,000 , 数据组数≤5000

对于50%的数据  S≤3,000,000,000 , 数据组数≤20000

对于80%的数据  S≤3,000,000,000,000 , 数据组数≤50000

对于100%的数据 S≤200,000,000,000,000, 数据组数≤80000

主要算法及思路:快速幂+枚举

/*
1、本题不难,就用到 一个快速幂 开long long即可过(第一次做的时候,没看数据范围,忘了这茬了,只得了53分,少的可怜) 
2、枚举时 保守起见
i:0->51
  j:0->35
    也许你觉得在你的电脑上 大于2^31 就输不出来了(我也这样认同);
但是,如果小了,一定会TLE(也就60分)。 
*/
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
long long s;
long long quick_pow(long long x,int n){//快速幂 
    if(n==0) return 1;
    else{
        while((n&1)==0){
            n>>=1;
            x*=x;
        }    
    }
    long long result=x;
    n>>=1;
    while(n!=0){
        x*=x;
        if((n&1)!=0){
            result*=x;
        }
        n>>=1;
    }
    return result;
}
int main(){
    while(scanf("%lld",&s)==1&&s){
        for(int i=0;i<=51;i++){
            for(int j=0;j<=35;j++){
                long long x=quick_pow(2,i);
                long long y=quick_pow(3,j);
                if(x+y==s){
                    printf("2^%d+3^%d=%lld
",i,j,s);
                    i=52;
                    break;
                }
                if(y>s) break;
            }
        }
    }
    return 0;
} 

3、洛谷 P1021 邮票面值设计

题目描述

给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤40)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1~MAX之间的每一个邮资值都能得到。

例如,N=3,K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大值,所以MAX=7,面值分别为1分、3分。

输入输出格式

输入格式:

2个整数,代表N,K。

输出格式:

2行。第一行若干个数字,表示选择的面值,从小到大排序。

第二行,输出“MAX=S”,S表示最大的面值。

输入输出样例

输入样例#1:

3 2

输出样例#1:

1 3
MAX=7
主要算法及思路:回溯枚举+dp验证
/*
1、一上来,没想很多,以为这是一个找规律的题,推出<1>直接开始写,
写完自己编了几组较大的测试数据,都不对(不对的代码下面也有),但<1>推导对了。就赶紧改呀~~
还好迷途知返,AC了 
2、正解思路: 
  采用回溯枚举的方法,枚举k种邮票,再用动规求出这k种邮票
  能组成出来的面值,进行取大处理。
  a[]储存枚举出来的邮票,ans[]储存最终答案(“1”不需要有) 
  f[i]表示当总面值为i时,至少用多少张a[]中的邮票 
*/
#include<cstdio>
#include<iostream>
#define INF 9999999
#define N 1000001
#define M 47
using namespace std;
int ans[M],a[M],f[N],maxl,n,k;
void dp(){
    int i=0;
    f[0]=0;
    while(f[i]<=n){//组成i这个面值所需的邮票数一定<=n,否则不满足题意 
        i++;
        f[i]=INF;//赋最大值,之后取小 
        for(int j=0;j<k&&i>=a[j];j++)//i>=a[j]组成面值i,其组成邮票a[j]必定<=i;由于a[]是按升序排列的,所以一旦a[j]>i,a[]后面的便都不符合要求 
            f[i]=min(f[i],f[i-a[j]]+1);
        if(i-1>maxl){//事前不知道f[i](i++了)是否<=n,所以不能用i来更新 
            maxl=i-1;//但是,i-1一定<=n 
            for(int j=0;j<k;j++)
                ans[j]=a[j];//更新答案 
        }
    }
}
void dfs(int t){//遍历 邮票种数 
    if(t==k){//[0..k-1]即k种邮票的一种方案已经产生,dp()去检验 
        dp();//所有情况都会被检验 
        return;
    }
    for(int i=a[t-1]+1;i<=a[t-1]*n+1;i++){//这个范围需要自己推导 见下<1> 
        a[t]=i;//就好比 样例答案中的 1 3(1首先一定会先被储存,并不会被更改,这一点毋庸置疑)
        //3是不知道的,你要储存起来,维护到k种,去检验,更新 
        dfs(t+1);//回溯 过程 
    }
}
int main(){

    scanf("%d%d",&n,&k);
    a[0]=1;//习惯[0..k-1] 
    dfs(1);//从第1种邮票开始遍历 
    for(int i=0;i<k;i++)
        printf("%d ",ans[i]);
    printf("
");
    printf("MAX=%d",maxl);
    return 0;
}
/*
第一次代码:
#include<cstdio>
#include<iostream>
using namespace std;
#define N 10010000
int n,k,p;
int f[N];
int main(){
    scanf("%d%d",&n,&k);
    if(k==1){
        printf("MAX=%d
",n);
        return 0;
    }
    f[1]=1;
    for(int i=2;i<=k;i++)
        f[i]=(i-1)*n+1;//<1>这里推导的规律是对的,不会自己想吧 
    for(int i=1;i<=k;i++)
        printf("%d ",f[i]);
    printf("
");
    printf("MAX=%d
",f[k]+n-1);
    return 0;
}*/


4、洛谷 P1120 小木棍

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入输出格式

输入格式:

输入文件共有二行。

第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤60

(管理员注:要把超过50的长度自觉过滤掉,坑了很多人了!)

第二行为N个用空个隔开的正整数,表示N根小木棍的长度。

输出格式:

输出文件仅一行,表示要求的原始木棍的最小可能长度

输入输出样例

输入样例#1:

9
5 2 1 5 2 1 5 2 1

输出样例#1:

6

 主要算法及思路:搜索+剪枝

/*
1、本题朴素枚举长度一般得40--70分,所以,我们必须要剪枝,才能AC
所标 //+.+ 为剪枝部分
2、简述各变量的意义
下面 i:maxx->sum中
       m:大木棍的长度(题目要求大木棍的长度的最小值) 
       num:大木棍的个数
dfs中
     len:当前凑出的大木棍的数量
     s:当前操作木棍的长度 
     pre:上一次选的小木棍的编号
3、实在听不明白:(原谅我尽力给你们讲明白了) 
自己手动模拟一遍,应该就好了  
*/
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 210
int n,m,x,num,a[N],ans,sum,flag,maxx;
bool f[N];
void dfs(int len,int s,int pre){//pre +.+
    if(flag) return;//+.+
    if(s==m){//又凑出一根来,个数+1,其余清零 
        len++;s=0;pre=0;
    }
    if(len==num-1){//+.+ 剩下一根一定可以组成该长度 
        flag=1;return ;//有合法答案 
    }
    for(int i=pre+1;i<=n;i++){
        if(f[i]==1||s+a[i]>m) continue;//+.+
        if(a[i]==a[i-1]&&f[i-1]==0) continue;//+.+
        f[i]=1;//可以被选 
        dfs(len,s+a[i],i);//递归搜索 
        if(flag) return ;//+.+
        f[i]=0;//回溯标记未选 
        if(s==0) break;//+.+
    }
}
int main(){
    //freopen("sticka.in","r",stdin);//看题目要求 
    //freopen("sticka.out","w",stdout);
    scanf("%d",&n);
    for(int i=1,x;i<=n;i++){
        scanf("%d",&x);
        if(x>50) m++;//Codevs--x>100;其余一般--x>50(注意看题) 
        else a[++num]=x;
    }
    n-=m;//减去不合法元素个数 
    for(int i=1;i<=n;i++){
        sum+=a[i];
        maxx=max(maxx,a[i]);//小棍中最长值 
    }
    sort(a+1,a+n+1,greater<int>());//+.+
    for(int i=maxx;i<=sum;i++){//大木棍长度一定在此范围内 
        if(sum%i==0){//必定成倍数关系 
            m=i;num=sum/i;flag=0;
            dfs(0,0,0);
            if(flag){
                printf("%d
",m);//答案 
                return 0;
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shenben/p/5567505.html