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;//注意是问还剩多少步汉诺塔才能运行完毕。
}
}