Gym 101196 (East Central North America Regional Contest 2016)

PROBLEM C D E G ARE INCLUDED.

Problem C The Key to Cryptography

题意:

         给出一种加密方式:密文 = 原文 + key(若 源key 的长度小于原文,则用原文补齐)的偏移量。现给出密文求原文。

思路:

         先利用 源key 译出原文的前几位,利用译出的原文翻译后面的原文。注意 源key 的长度可能大于原文

代码:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    string str1,str2;
    while(cin>>str1>>str2){
        string ans;
        int len1=str1.size(),len2=str2.size();
        int pos=-1;
        for(int i=0;i<len2&&pos<len1-1;i++)
            ans+=(str1[++pos]-str2[pos]+26)%26+'A';
        for(int i=0;pos<len1-1;i++)
            ans+=(str1[++pos]-ans[i]+26)%26+'A';
        cout<<ans<<endl;
    }
}

Problem D Lost in Translation

题意:

         给出一个图,从顶点开始(English),寻找 到每个点优先满足路径最短时的最小花费。

思路:

         所谓路径最短优先,其实就是 bfs 逐层搜索。若同层中有指向同一节点但花费不同的边,取最小即可。每搜完一层统计一遍 ans,给搜到的点打上 vis 标记即可。

代码:

(略丑)

#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;

map <string,int> has;
vector <int> mp[105];
int n,m;
int color[105];
bool vis[105];
int cost[105][105];
queue <int> que;
int ans[105],cut;
stack <int> here;
void ini(){
    has.clear();
    cut=0;
    has["English"]=0;
    for(int i=0;i<=101;i++){
        mp[i].clear();
        color[i]=INF;
        vis[i]=0;
        ans[i]=INF;
    }
}
int make_color(){
    int fans=0;
    que.push(0);
    int pos,len,now;
    color[0]=0;
    vis[0]=1;
    int floor=0;
    while(!que.empty()){
        pos=que.front();
        que.pop();
        if(color[pos]>floor){
            while(!here.empty()){
                now=here.top();
                here.pop();
                if(!vis[now]){
                    fans+=ans[now];
                    cut++;
                }
                vis[now]=1;
                //cout<<now<<": "<<fans<<endl;
            }
            floor++;
        }
        len=mp[pos].size();
        for(int i=0;i<len;i++){
            now=mp[pos][i];
            if(color[pos]<color[now]){
                color[now]=color[pos]+1;
                que.push(now);
                if(!vis[now]){
                    ans[now]=min(ans[now],cost[pos][now]);
                    here.push(now);
                }
            }
        }
    }
    return fans;
}

int main()
{
    int x;
    string str,str1,str2;
    int pos1,pos2;
    while(cin>>n>>m){
        ini();
        for(int i=1;i<=n;i++){
            cin>>str;
            has[str]=i;
        }
        for(int i=0;i<m;i++){
            cin>>str1>>str2>>x;
            pos1=has[str1];pos2=has[str2];
            mp[pos1].push_back(pos2);
            mp[pos2].push_back(pos1);
            cost[pos1][pos2]=x;
            cost[pos2][pos1]=x;
        }
        int ffans=make_color();
        if(cut==n)
            cout<<ffans<<endl;
        else
            cout<<"Impossible"<<endl;
    }
}

Problem E Red Rover

题意:

          给出一个串,现允许使用一个字符代替一部分串以减小串的长度。问可以实现的最小的串的长度是多少。

思路:

          其实就是找符合 子串重复次数*子串长度 最大的子串。由于数据量很小,直接暴力可过。在求重复次数时用KMP加速一下更快。

代码:

#include <iostream>
#include <map>
#include <cstring>
#include <set>
#include <cstdio>
#include <string>
using namespace std;

int get(string str1,string str2){
    int ans=0;
    int len=str2.size();
    int ll=str1.size();
    for(int i=0;i<len+1-ll;i++){
        if(str1==str2.substr(i,ll)){
            ans++;
            i+=ll-1;
        }
    }
    return ans;
}
int main()
{
    string str1,str2;
    int ans[105][105],tlen;
    while(cin>>str1){
        memset(ans,0,sizeof(ans));
        int len=str1.size();
        int fans=len,fansi,fansj;
        for(int i=1;i<=len;i++){
            for(int j=0;j<len+1-i;j++){
                str2=str1.substr(j,i);
                ans[i][j]=get(str2,str1);
                tlen=len-ans[i][j]*i+ans[i][j]+i;
                if(fans>tlen){
                    fans=tlen;
                    fansi=i;
                    fansj=j;
                }
            }
        }
        cout<<fans<<endl;
    }
    return 0;
}


Problem G That’s One Hanoi-ed Teacher

题意:

         如题图给出题目要求的汉诺塔运行方式(就是最优的移动方式)。给出三根柱子上的盘子数及盘子的序号。问当前状态是否合法,若合法输出最少剩余几步即可完成汉诺塔的运行。

思路:

         没什么好的思路,故先打表,打表程序如下:

#include <bits/stdc++.h>
using namespace std;
int pos[150];
int n;
int len[4];
queue <int> que[150];
int move(int x,char a,char b)
{
    //printf("number..%d..form..%c..to..%c\n",x,a,b);
    pos[x]=b-'A'+1;
    for(int i=1;i<=n;i++)
        que[i].push(pos[i]);
}

int mission(int x,char a,char b,char c)
{
    if(x==1)
    move(1,a,c);
    else
    {
        mission(x-1,a,c,b);//将x-1从a借c移到b。
        move(x,a,c);
        mission(x-1,b,a,c);//将x-1从b借a移到c。
    }
    return 0;
}
int main()
{
    while(cin>>n){
        for(int i=1;i<=n;i++){
            pos[i]=1;
            que[i].push(1);
        }
        mission(n,'A','B','C');
        for(int i=1;i<=n;i++){
            cout<<i<<":";
            while(!que[i].empty()){
                cout<<' '<<que[i].front();
                que[i].pop();
            }
            cout<<endl;
        }
    }
    return 0;
}
打表结果:

可以发现规律,
首先对于最大的盘子 MAX,其前一半为 1 后一半为 3
     若第 N 个盘子的位置 和 N 的前半区间相同,那么取前一半区间向下寻找
         且在 N 前一半区间内,第 N - 1 个盘子的前一半和 N 前一半区间相同。
         对于第 N - 1 个盘子的后一半,当 N - 1 和 MAX 的奇偶性相同时按照 “321” 序排列,不同时按照“123”序。
             即:
             当 MAX 为奇数时,若 N - 1 为偶数则前后一半按照 “123” 的顺序排列
           (即若第 N - 1 个盘子的前一半为 1 ,那么后一半为 2。前一半是 3 ,那么后一半为 1)
                                              若 N - 1 为奇数则前后一半按照 “321” 的顺序排列
           (即若第 N - 1 个盘子的前一半为 1 ,那么后一半为 3。前一半是 3 ,那么后一半为 2)
             当 MAX 为偶数时,若 N - 1 为偶数则前后一半按照 “321” 的顺序排列
                                              若 N - 1 为奇数则前后一半按照 “123” 的顺序排列                                                     
     同理:
     若第 N 个盘子的位置 和 N 的后半区间相同,那么取后一半区间向下寻找
         且在 N 后一半区间内,第 N - 1 个盘子的后一半和 N 后一半区间相同。
         对于第 N - 1 个盘子的前一半,当 N - 1 和 MAX 的奇偶性相同时按照 “321” 序排列,不同时按照“123”序。
             即:
             当 MAX 为奇数时,若 N - 1 为偶数则前后一半按照 “123” 的顺序排列
           (即若第 N - 1 个盘子的后一半为 1 ,那么前一半为 3。后一半是 3 ,那么后一半为 2)
                                              若 N - 1 为奇数则前后一半按照 “321” 的顺序排列
           (即若第 N - 1 个盘子的后一半为 1 ,那么前一半为 2。后一半是 3 ,那么前一半为 1)
             当 MAX 为偶数时,若 N - 1 为偶数则前后一半按照 “321” 的顺序排列
                                              若 N - 1 为奇数则前后一半按照 “123” 的顺序排列



根据以上规律:我们可以预测下一个盘子可能的位置,逐层缩小解的范围,到最后一层得到答案。

代码:

#include <bits/stdc++.h>
using namespace std;
long long pos[60],n,x,two[60],ans,num,low,high,fans,tot;
bool lr;
int main()
{
    x=1;
    for(int i=0;i<=50;i++){
        two[i]=x;
        x*=2;
    }
    //...............以上先打好表求2的幂
    while(cin>>n){
        num=0;
        bool flag=1;
        lr=0;
        fans=0;
        for(int i=1;i<=3;i++){
            if(i!=1) cin>>n;
            num+=n;
            for(int j=1;j<=n;j++){
                cin>>x;
                pos[x]=i;
            }
        }
        tot=num;
        //............以上记录盘子的位置
        low=1;high=3;
        while(num&&flag){
            if(pos[num]==low){
                lr=0;
            }else if(pos[num]==high){
                lr=1;
            }else{
                flag=0;
            }
            //..................以上判断是在前一半还是在后一半,若都不在说明盘面是非法的
            if((tot%2==1&&num%2==0)||(tot%2==0&&num%2==1)){
                if(lr==0){
                    low=low;
                    if(low==1) high=3;
                    else high=low-1;
                }else{
                    high=high;
                    if(high==3) low=1;
                    else low=high+1;
                    fans+=two[num-1];
                }
            }else{
                if(lr==0){
                    low=low;
                    if(low==3) high=1;
                    else high=low+1;
                }else{
                    high=high;
                    if(high==1) low=3;
                    else low=high-1;
                    fans+=two[num-1];
                }
            }
            //...................以上是确定第 N - 1 个盘的前一半和后一半是哪两个柱子。
            //同时统计答案位置
            num--;
        }
        if(flag==0) cout<<"No"<<endl;
        else cout<<two[tot]-fans-1<<endl;//注意是问还剩多少步汉诺塔才能运行完毕。
    }
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值