algorithm 题集三 (16.05.24)

这篇博客记录了作者近期在算法训练中的题目与解题心得,涉及动态规划、贪心算法和几何问题。文章介绍了多个在线编程竞赛的题目,如Ancient Cipher(规律)、Decode(reverse函数)和Mischievous Mess Makers(贪心),并给出了思路分析,包括如何利用规律、逆序操作和贪心策略解决问题。
摘要由CSDN通过智能技术生成

练习的故事仍然继续下去,今天写下的题目难度适中,记录自己近段时间的训练成果。涉及数学,动态规划,字符串问题。

poj 2159 Ancient Cipher(规律)

http://poj.org/problem?id=2159
大意:密码加密方法,两种加密方式,第一种,替换加密:Substitution cipher changes all occurrences of each letter to some other letter. Substitutes for all letters must be different(这就是当时做题坑的我一瘸一拐的 一句话).
第二种,排列加密:字符串乱序一下。
分析:那条语句就是精华啊啊啊,告诉我们,在得到每一个字符的出现次数后只需要排列比较是否一样就行。

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

char s1[105],s2[105];
int t1[30],t2[30];

int main()
{
    //freopen("cin.txt","r",stdin);
    while(~scanf("%s",s1)){
        scanf("%s",s2);
        memset(t1,0,sizeof(t1));
        memset(t2,0,sizeof(t2));
        int len1=strlen(s1),len2=strlen(s2);
        for(int i=0;i<len1;i++) t1[s1[i]-'A']++;
        for(int i=0;i<len2;i++) t2[s2[i]-'A']++;
        bool same=1;
        sort(t1,t1+30);    sort(t2,t2+30);
        for(int i=0;i<30;i++){
            if(t1[i]!=t2[i]){ same=0;  break; }
        }
        if(same) puts("YES");
        else puts("NO");
    }
    return 0;
}

nefu 1174 Decode(reverse函数)

http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=1174
大意:将字符串中的小串逆序,然后输出,如:
Sample Input
i evol uoy
doog emag
Sample Output
i love you
good game

linux下用不了个gets(),我的版本号是ubuntu 16.04 LTS:

main.cpp: In functionint main()’:
main.cpp:10:12: warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]
     while(~gets(str)){
            ^
In file included from main.cpp:1:0:
/usr/include/stdio.h:638:14: note: declared here
 extern char *gets (char *__s) __wur __attribute_deprecated__;
              ^
main.cpp:10:12: warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]
     while(~gets(str)){
            ^
In file included from main.cpp:1:0:
/usr/include/stdio.h:638:14: note: declared here
 extern char *gets (char *__s) __wur __attribute_deprecated__;
              ^
main.cpp:10:20: warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]
     while(~gets(str)){
                    ^
In file included from main.cpp:1:0:
/usr/include/stdio.h:638:14: note: declared here
 extern char *gets (char *__s) __wur __attribute_deprecated__;
              ^
main.cpp:10:20: error: wrong type argument to bit-complement
     while(~gets(str)){
  

掌握reverse函数的用法即可。

#include <stdio.h>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <stdlib.h>
using namespace std;

char str[1005],s[1005];
int main(){
    while(~scanf("%c",&str[0])){
        if(str[0]==EOF) break;
        int L=1;
        while(scanf("%c",&str[L++]) && str[L-1]!='\n');
        str[L-1]=0;
        int top=0;
        for(int i=0;str[i];i++){
            if(str[i]==' '){
                s[top]=0;
                reverse(s,s+top);  // 系统函数实现翻转
                printf("%s ",s);
                top=0;
            }
            else s[top++]=str[i];
        }
        s[top]=0;
        reverse(s,s+top);  // 系统函数实现翻转
        printf("%s\n",s);
    }
    return 0;
}

问题拓展:
将字符串整体逆序,小串不逆序

asfd dfds fgh jkj
jkj fgh dfds asfd

#include <stdio.h>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
char str[100][105],s[105];
char L[10005];
int main()
{
    while(gets(L)){
        int top=0,len=0;
        for(int i=0;L[i];i++){
            if(L[i]==' '){
                s[top]=0;
                strcpy(str[len++],s);
                top=0;
            }
            else s[top++]=L[i];
        }
        s[top]=0;
        strcpy(str[len++],s);
        reverse(str,str+len);
        for(int i=0;i<len-1;i++){
            printf("%s ",str[i]);
        }
        printf("%s\n",str[len-1]);
    }
    return 0;
}

codeforces 645B. Mischievous Mess Makers(贪心)

http://codeforces.com/problemset/problem/645/B
大意:给出一个1-n的序列,最多进行k次交换,问最大的逆序数是多少?
分析:为了得到最大的逆序数,我们需要交换右边最大的数字和左边最小的数字。最开始的数字有n个,交换一次剩下n-2个数字……如果n是偶数,最后剩下是2个;是奇数的話最后剩下3个。每交换一次,产生的逆序数是n-1+1+1+……+0=2n-3
eg:
1 2 3 4 5
5 2 3 4 1
–>
4 1 1 1 0
设最小n是minm=min(2,n-2k+2) OR min(3,n-2k+2),交换次数c= (n-minm)/2+1
所以最后的逆序数就是2(n+n-2+……+minm)-3c

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;

int main()
{
    LL n,k;
    while(cin>>n>>k){
        if(n==1) {
            puts("0");   continue;
        }
        LL minm=0;
        if(n&1) minm=max(3LL,n-2*k+2);
        else minm=max(2LL,n-2*k+2);  //min n
        LL c=(n-minm)/2+1;  //swap counts
        LL s=c*(n+minm)/2;
        s=s*2-3*c;
        printf("%I64d\n",s);
    }
    return 0;
}

nefu 1173 Dipper Landlord(规律)

http://acm.nefu.edu.cn/JudgeOnline/problemShow.php?problem_id=1173
大意:斗地主游戏。每一次游戏有一个地主两个农民, x=6×2y ,地主赢了加分2x,农民减分x, 农民赢了都加分x, 地主减分2x。现在给出三个数字,问是否合法。
分析:地主每次的变化量是2x,两个农民的变化量是每人x。这样看来它们任意两个数字的差值的绝对值是3x的倍数。同时三个数的和是3000。
当时的思路是比较复杂的。我想着地主,农民,农民: x(2k) -xk -xk
x需要满足的形式是 6×2y , 然后直接判断即可。代码写了老长了,结果还错了T_T

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

int main()
{
    int r[3];
    while(cin>>r[0]>>r[1]>>r[2]){
         int sum=r[0]+r[1]+r[2];
         int d1=r[0]-r[1];
         int d2=r[1]-r[2];
         int d3=r[0]-r[2];
         if(sum==3000 && d1%18==0 && d2%18==0 && d3%18==0) puts("Yes");
         else puts("No");
    }
    return 0;
}

poj 2262 Goldbach’s Conjecture(简单)

http://poj.org/problem?id=2262
大意:求解6-1000000内的偶数能否写成两个素数之和。
分析:练手

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N=1e6+10;
vector<int> prim;
int cnt;
bool vis[N];
void getprim(){
    for(int i=2;i<N;i++){
        if(!vis[i]) prim.push_back(i);
        cnt=prim.size();
        for(int j=0;j<cnt && prim[j]*i<N;j++){
            vis[i*prim[j]]=1;
            if(i%prim[j]==0) break;
        }
    }
}
int main()
{
    getprim();
    int n;
    while(cin>>n&&n){
        bool tag=0;
        for(int i=1;prim[i]+prim[i]<=n;i++){
            int g=n-prim[i];
            if(vis[g]==0) {
                printf("%d = %d + %d\n",n,prim[i],g);
                tag=1;
                break;
            }
        }
        if(tag==0) puts("Goldbach's conjecture is wrong.");
    }
    return 0;
}

nyist 368 最长公共子序列(简单)

http://acm.nyist.net/JudgeOnline/problem.php?pid=36&rec=rec
只要求输出字符串的长度:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
char s1[1005],s2[1005];
int g[1005][1005];
int main(){
    //freopen("cin.txt","r",stdin);
    int t;
    cin>>t;
    while(t--){
        scanf("%s%s",s1+1,s2+1);
        memset(g,0,sizeof(g));
    for(int i=1;s1[i];i++){
        for(int j=1;s2[j];j++){
            if(s1[i]==s2[j]){
            g[i][j]=g[i-1][j-1]+1;
        }
        else {
            g[i][j]=max(g[i-1][j],g[i][j-1]);  //当两者不同时,两个串的当前字符都可能成为LCS的一部分
        }
        }
    }
    int len1=strlen(s1+1),len2=strlen(s2+1);
    printf("%d\n",g[len1][len2]);
    }
    return 0;
}



问题拓展:
要求输出最长的相同字符子串:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
char s1[1005],s2[1005];
int g[1005][1005];
int dir[1005][1005];
int main(){
    //freopen("cin.txt","r",stdin);
    int t;
    cin>>t;
    while(t--){
        scanf("%s%s",s1+1,s2+1);
        memset(g,0,sizeof(g));
    memset(dir,-1,sizeof(dir));
    for(int i=1;s1[i];i++){
        for(int j=1;s2[j];j++){
            if(s1[i]==s2[j]){
            g[i][j]=g[i-1][j-1]+1;
            dir[i][j]=1;   // 左斜上方
        }
        else {
            if(g[i-1][j]>g[i][j-1]) {  g[i][j]=g[i-1][j];  dir[i][j]=0; }  //上方
            else {  g[i][j]=g[i][j-1];   dir[i][j]=2;  }  //左边
        }
        }
    }
    int len1=strlen(s1+1),len2=strlen(s2+1);
    printf("%d\n",g[len1][len2]);
        int x=len1, y=len2, top=0;
    char str[1005];
    memset(str,0,sizeof(str));
    while(x>0 && y>0){
        if(s1[x]==s2[y])  str[top++]=s1[x];
        if(dir[x][y]==0) x--;
        else if(dir[x][y]==1){  x--;  y--; }
        else if(dir[x][y]==2){  y--; }
    }
    while(top>0){
        printf("%c",str[top-1]);
        top--;
    }
    puts("");
    }
    return 0;
}

codeforces 645A. Amity Assessment(规律)

http://codeforces.com/problemset/problem/645/A
大意:
这里写图片描述
能否通过变换使得两者具有相同的结构?
分析:空格相邻的字母来填补空格不会变化的是顺时针的字母顺序。于是整个问题就变成了数组位移问题。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char B[2][5],E[2][5];
char s1[5],s2[5];
void moves(char ss[]){
    char ch=ss[2];
    ss[2]=ss[1]; ss[1]=ss[0]; ss[0]=ch;
}
int main()
{
    while(~scanf("%s",B[0])){
        scanf("%s%s%s",B[1],E[0],E[1]);
        s1[0]=B[0][0];  s1[1]=B[0][1]; s1[2]=B[1][1];  s1[3]=B[1][0];
        s2[0]=E[0][0];  s2[1]=E[0][1]; s2[2]=E[1][1];  s2[3]=E[1][0];
        for(int i=0;i<4;i++){
            if(s1[i]=='X') {
                for(int j=i;j<4;j++) s1[j]=s1[j+1];
            }
            if(s2[i]=='X') {
                for(int j=i;j<4;j++) s2[j]=s2[j+1];
            }
        }
        bool same=0;
        for(int i=0;i<3;i++){
            if(strcmp(s1,s2)==0) { same=1;  break; }
            moves(s1);
        }
        if(same) puts("YES");
        else puts("NO");
    }
    return 0;
}

hdu 3853 LOOPS(概率dp)

http://acm.hdu.edu.cn/showproblem.php?pid=3853
大意:逃离迷宫,从左上角出发,到达右下角算是成功逃脱。每一个点有三条路径可选,自身,右边,下边,他们的选择概率分别给出,每一次跳跃格子都将花费2份魔力,问逃离迷宫的花费魔力的期望

分析:
假设点(i,j)选择自身,右边,下边的概率分别是
p[i][j][0] , p[i][j][1] , p[i][j][2]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值