algorithm 题集二 (16.04.30)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/theArcticOcean/article/details/51286597

继上一篇博文讲,有一类题不像上一篇博文的题那样简单但接触后感觉不难,这类题单独做了一个题集二。

POJ 2229 Sumsets

http://poj.org/problem?id=2229
大意:求解一个数字分解成2的幂的方案数
7:
1) 1+1+1+1+1+1+1
2) 1+1+1+1+1+2
3) 1+1+1+2+2
4) 1+1+1+4
5) 1+2+2+2
6) 1+2+4
分析:注意看第一列,容易知道6和7的分解数一样,即奇数和小于它的最大偶数的一样。
再看第四行,看出来前四种方案都是4或者说5的分解,再将
5) 1+2+2+2
6) 1+2+4
看成
5) 1+1+1
6) 1+2
即3或者说2的分解。
前四种分解是n-2的分解
后两种是n>>1的分解
所以dp[n]=dp[n-2]+dp[n>>1]

#include <iostream>
#include <cstdio>
using namespace std;
const int mod=1e9;
const int N=1e6+10;   // 试了试极限,5e8超内存
/*int get(int n){    // 栈溢出
    if(n==1 || n==0)
        return 1;
    return (get(n-2)+get(n>>1))%mod;
}*/
int dp[N];
int main()
{
    dp[0]=dp[1]=1;
    for(int i=2;i<N;i++){
        dp[i]=(dp[i-2]+dp[i>>1])%mod;
    }
    int n;
    while(cin>>n){
        printf("%d\n",dp[n]);
    }
    return 0;
}

nefu 355 合成陨石

nefu 355 合成陨石
http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=355
fz大学化学系的学生们最近发现了一种奇怪的陨石,这些陨石通过化学反应合成,会放出惊人的破坏力量。为了储存方便,化学系的学生们决定把这些陨石碎块合成一个大的陨石块。每一次合并,可以把两个陨石合成一个,放出的破坏能量是两个陨石的质量之和,而新产生的陨石质量是这两个陨石的质量之和。
为了将这次试验的破坏效果减少到最低,他们想请你帮忙,计算一下可以达到的最小破坏能量值。
分析:优先队列

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;

int main()
{
    int n,a;
    while(cin>>n){
        priority_queue<int,vector<int>,greater<int> > que;
        for(int i=0;i<n;i++) {
            scanf("%d",&a);
            que.push(a);
        }
        if(n==1) {
            printf("%d\n",a);
            continue;
        }
        int ans=0,get=0;
        while(!que.empty()){
            get=que.top();
            que.pop();
            if(que.empty()) break;
            get=get+que.top();
            que.pop();
            ans=ans+get;
            que.push(get);
        }
        printf("%d\n",ans);
    }
    return 0;
}

nefu 831 统计good

http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=831
给你一些英文单词,你能统计出good有多少个吗?
sample_input
2
i am a good boy!
goof good good dood good
sample_output
1
3
分析:借助强大的C++字符流string, stringstream。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
using namespace std;

int main()
{
    int t;
    string line,word;
    cin>>t;
    getchar();
    while(t--){
        getline(cin,line);
        int len=line.length();
        for(int i=0;i<len;i++){
            if(isalpha(line[i])==false) line[i]=' ';  //判断是否是字母
        }     // sstream是以空格为分界的。
        stringstream stream(line);
        int ans=0;
        while(stream>>word) if(word=="good")   ans++;
        printf("%d\n",ans);
    }
    return 0;
}

nyist 412 Same binary weight

http://acm.nyist.net/JudgeOnline/problem.php?pid=412
大意:给出一个数字,其权重是二进制表示的1的个数。求解最小的数字,它大于给定的数字,且权重相同。

分析:使用STL的bitset解决二进制问题很便利。为保证权重不变,只需把原来的1变化位置,即进位和退位。比如:12 —— 1100 ——> 10001
45 —— 101101 ——>110011

#include <iostream>
#include <cstdio>
#include <bitset>
using namespace std;
typedef long long LL;
int main()
{
    LL n;
    while(~scanf("%lld",&n)){
        bitset<64> bit(n);
        //for(int i=0;i<35;i++) cout<<bit[i]; //展示出来和常见的二进制表示是相反的
        int zero=-1,one=-1;
        for(int i=0;i<64;i++){
            if(bit[i]==1) one=i;
            else if(one>=0 && bit[i]==0){
                zero=i;
                break;
            }
        }
        bit[zero]=1;
        bit[one]=0;
        int sum=0;
        for(int i=0;i<one;i++){
            if(bit[i]==1){
                sum++;
                bit[i]=0;
            }
        }
        for(int i=0;i<sum;i++){
            bit[i]=1;
        }
        cout<<bit.to_ulong()<<endl;
    }
    return 0;
}

POJ 3748 位操作

http://poj.org/problem?id=3748
大意:假设你工作在一个32位的机器上,你需要将某一个外设寄存器的第X位设置成0(最低位为第0位,最高位为第31位),将第Y位开始的连续三位设置成110(从高位到低位的顺序),而其他位保持不变。对给定的寄存器值R,及X,Y,编程计算更改后的寄存器值R。

#include <iostream>
#include <cstdio>
using namespace std;

int main()
{
    int n,x,y;
    while(~scanf("%x,%d,%d",&n,&x,&y)){
        int p=1;
        p=~(p<<x);
        n=n&p;  // -
        p=1;
        p=p<<y;
        n=n|p; // +
        p=1;
        p=p<<(y-1);
        n=n|p;
        p=1;
        p=~(p<<(y-2));
        n=n&p;
        printf("%x\n",n);
    }
    return 0;
}

POj 1284 Primitive Roots

http://poj.org/problem?id=1284
大意:求出N内的原根的个数。
关于原根: gimod pgjmod p, 其中i≠j且i,j在1至(p-1)之间,则g为p的原根。
分析:
相关定理:
1)所有的奇素数都是有原根的
2)一个数n有原根,那么他有phi(phi(n))个模n不同余的原根(n是否是素数都可用)
3)一个素数有原根,则有phi(n-1)个原根
第二条就是解决问题的关键

nyist 19 擅长排列的小明

http://acm.nyist.net/JudgeOnline/problem.php?pid=19
大意:求出n个数字中m个数字的组合的全排列情况
例如:
4 2
12
13
14
21
23
24
31
32
34
41
42
43

分析:使用C++里的next_permutation()写出所有的全排列情况,输出前m数字即可。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int n,m;
char s[10]="123456789";
int main()
{
    int t;
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&m);
        char s1[10],s2[10],s3[10];
        strcpy(s1,s);
        s1[m]=0;
        printf("%s\n",s1);
        strcpy(s2,s);
        while(next_permutation(s2,s2+n)){
            strcpy(s3,s2);
            s3[m]=0;
            if(strcmp(s1,s3)){
                strcpy(s1,s3);
                printf("%s\n",s1);
            }
        }
    }
    return 0;
}

51nod 1266 蚂蚁

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1266
n 只蚂蚁以每秒1cm的速度在长为Lcm的竿子上爬行。当蚂蚁爬到竿子的端点时就会掉落。由于竿子太细,两只蚂蚁相遇时,它们不能交错通过,只能各自反向爬 回去。对于每只蚂蚁,我们知道它距离竿子左端的距离xi,但不知道它当前的朝向。请计算各种情况当中,所有蚂蚁落下竿子所需的最短时间和最长时间。
这里写图片描述
关键: 1和2相遇,掉头后,2看做1,1看做2,这样继续前进了。

51nod 1279 扔盘子

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1279
有一口井,井的高度为N,每隔1个单位它的宽度有变化。现在从井口往下面扔圆盘,如果圆盘的宽度大于井在某个高度的宽度,则圆盘被卡住(恰好等于的话会下去)。
盘子有几种命运:1、掉到井底。2、被卡住。3、落到别的盘子上方。
盘子的高度也是单位高度。给定井的宽度和每个盘子的宽度,求最终落到井内的盘子数量。
这里写图片描述
如图井和盘子信息如下:
井:5 6 4 3 6 2 3
盘子:2 3 5 2 4

最终有4个盘子落在井内。
分析:dp,追踪最小值

#include <iostream>
#include <cstdio>
using namespace std;
const int N=5e4+5;
int dp[N],q[N];
int main()
{
    //freopen("cin.txt","r",stdin);
    int n,m,x;
    while(cin>>n>>m){
        for(int i=1;i<=n;i++) {
            scanf("%d",&x);
            if(i==1) dp[i]=x;
            else  dp[i]=min(x,dp[i-1]);
        }
        for(int i=0;i<m;i++){
            scanf("%d",&q[i]);
        }
        int cur=n,ans=0;
        for(;cur>0;){
            if(q[ans]>dp[cur]) cur--;  //卡住了,不知道是否在内部
            else ans++, cur--;
        }
        printf("%d\n",ans);
    }
    return 0;
}

poj 2773 Happy 2006

http://poj.org/problem?id=2773
寻找第K个和M互质的数字:
分析:容斥原理+二分查找

#include <iostream>
#include <cstdio>
using namespace std;
typedef  long long LL;
const LL N=1e6,M=1e13*1LL;
LL pri[N],cnt;
bool vis[N];
void getpri(LL Max){
    cnt=0;
    for(LL i=2;i<=Max;i++){
          if(!vis[i]) pri[cnt++]=i;
          for(LL j=0;j<cnt&&i*pri[j]<=Max;j++){
                vis[pri[j]*i]=1;
                if(i%pri[j]==0) break;
          }
    }
}
LL fac[N],top;
void solve(LL num){
      top=0;
      for(int i=0;pri[i]*pri[i]<=num;i++){
             if(num%pri[i]==0){
                   fac[top++]=pri[i];
                   while(num%pri[i]==0) num/=pri[i];
             }
      }
      if(num>1) fac[top++]=num;
}
LL Sum(LL x){
      LL g=0;
      for(LL i=1;i<(1LL<<top);i++){
            LL red=0,dd=1;
            for(LL j=0;j<top;j++){
                if(i&(1LL<<j)){
                    red++;
                    dd=dd*fac[j];
                }
            }
            if(red&1)  g=g+x/dd;
            else g=g-x/dd;
      }
      return x-g;
}
LL midfind(LL k){
     LL l=1,r=M,mid,ans=1;
     while(l<=r){
           mid=(l+r)>>1;
           if(Sum(mid)>=k){
                   ans=mid;
                   r=mid-1;
           }
           else l=mid+1;
     }
     return ans;
}
int main()
{
     //freopen("cin.txt","r",stdin);
     LL m,k;
     getpri(N);
     while(~scanf("%lld%lld",&m,&k)){
          solve(m);
          printf("%lld\n",midfind(k));
     }
    return 0;
}

CF 248B Chilly Willy

http://codeforces.com/problemset/problem/248/B
大意:求出最小能被2,3,5,7整除的N位数。
分析:四个质数的最小公倍数是210。于是N位数 210×10n3 一定可以被整除。

但是他不是最小的,最小数可以表示成:210×10n3210k
进一步处理成: (100+110)×10n3210k=10n1+11×10n2%210

nefu 887 循环小数化分数

http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=887
分析:
对于一个循环小数,如

0.29(34)100x=29.3410000x=2934.349900x=293429=2905x=29059900

故求解循环部分和非循环部分的值及10^n后,按以上做差思路消除循环部分。得到X的分子和分母,化简即可。纯循环小数按上诉过程推导得到 value999n 非循环小数就不说了。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=1e5+5;
char str[N];
LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
int main()
{
    while(~scanf("%s",str)){
        int len=strlen(str);
        LL f1=0,f2=0,z1=1,z2=1;
        bool loop=0;
        for(int i=2;i<len;i++){
            if(str[i]=='('){
                loop=1;
                continue;
            }
            if(loop==0){
                if(f1==0) f1=str[i]-'0';
                else {
                    f1=f1*10+(str[i]-'0');
                }
                z1=z1*10;
            }
            if(str[i]==')') break;
            if(loop==1){
                if(f2==0) f2=str[i]-'0';
                else {
                    f2=f2*10+(str[i]-'0');
                }
                //f2=f2*10+(str[i]-'0');
                z2=z2*10;
            }
        }
        if(z1>1&&z2>1){
            LL m=z1*z2-z1;
            LL n=f1*z2+f2-f1;
            LL d=gcd(n,m);
            printf("%lld/%lld\n",n/d,m/d);
        }
        else if(z1==1&&z2>1){
            LL m=z2-1;
            LL d=gcd(f2,m);
            printf("%lld/%lld\n",f2/d,m/d);
        }
        else if(z1>1&&z2==1){
            LL d=gcd(f1,z1);
            printf("%lld/%lld\n",f1/d,z1/d);
        }
    }
    return 0;
}

UVA 10025 The ? 1 ? 2 ? … ? n = k problem

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=966
大意:?1?2?3……?n=k,?可以取+或-,求解最小的n
分析:设非负数的和是s1,负数的和是s2,那么有

S1+S2=kS1S2=(n+1)n22S1=k+n(n+1)22S2=kn(n+1)2

所以,kn(n+1)20 , 且为偶数

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页