【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】

时间:2023-03-08 16:12:49

目录:1、潜伏者 【map】 2、Hankson的趣味题【数论】3、mayan游戏【dfs】


题目:

1. 潜伏者
(spy.pas/c/cpp)
【问题描述】
R 国和S 国正陷入战火之中,双方都互派间谍,潜入对方内部,伺机行动。
历经艰险后,潜伏于S 国的R 国间谍小C 终于摸清了S 国军用密码的编码规则:
1、 S 国军方内部欲发送的原信息经过加密后在网络上发送,原信息的内容
与加密后所的内容均由大写字母‘A’—‘Z’构成(无空格等其他
字母)。
2、 S 国对于每个字母规定了对应的“密字”。加密的过程就是将原信息中
的所有字母替换为其对应的“密字”。
3、 每个字母只对应一个唯一的“密字”,不同的字母对应不同的“密字”。
“密字”可以和原字母相同。
例如,若规定‘A’的密字为‘A’,‘B’的密字为‘C’(其他字母及密字略),则
原信息“ABA”被加密为“ACA”。
现在,小C 通过内线掌握了S 国网络上发送的一条加密信息及其对应的原信息。小C
希望能通过这条信息,破译S 国的军用密码。小C 的破译过程是这样的:扫描原信息,对
于原信息中的字母x(代表任一大写字母),找到其在加密信息中的对应大写字母y,并认
为在密码里y 是x 的密字。如此进行下去直到停止于如下的某个状态:
1、 所有信息扫描完毕,‘A’—‘Z’所有26 个字母在原信息中均出现过
并获得了相应的“密字”。
2、 所有信息扫描完毕,但发现存在某个(或某些)字母在原信息中没有出
现。
3、 扫描中发现掌握的信息里有明显的自相矛盾或错误(违反S 过密码的编
码规则)。例如某条信息“XYZ”被翻译为“ABA”就违反了“不同
字母对应不同密字”的规则。
在小C 忙得头昏脑胀之际,R 国司令部又发来电报,要求他翻译另外一条从S 国刚刚
截取到的加密信息。现在请你帮助小C:通过内线掌握的信息,尝试破译密码。然后利用破
译的密码,翻译电报中的加密信息。
【输入】
输入文件名为spy.in,共3 行,每行为一个长度在1 到100 之间的字符串。
第1 行为小C 掌握的一条加密信息。
第2 行为第1 行的加密信息所对应的原信息。
第3 行为R 国司令部要求小C 翻译的加密信息。
输入数据保证所有字符串仅由大写字母‘A’—‘Z’构成,且第1 行长度与第2 行相
等。
【输出】
输出文件spy.out 共1 行。

若破译密码停止时出现2,3两种情况,请你输出“Failed”(不含引号,注意首字母大写,其它小写)。
否则请输出利用密码翻译电报中加密信息后得到的原信息。
【输入输出样例1】
spy.in                              spy.out
AA                                Failed
AB
EOWIE          
【输入输出样例1说明】
原信息中的字母‘A’和‘B’对应相同的密字,输出“Failed”。
【输入输出样例2】
spy.in                              spy.out
QWERTYUIOPLKJHGFDSAZXCVBN                Failed
ABCDEFGHIJKLMNOPQRSTUVWXY
DSLIEWO                      
【输入输出样例2说明】
字母‘Z’在原信息中没有出现,输出“Failed”。
【输入输出样例3】
spy.in                                                                                                     spy.out
MSRTZCJKPFLQYVAWBINXUEDGHOOILSMIJFRCOPPQCEUNYDUMPP                 NOIP
YIZSDWAHLNOVFUCERKJXQMGTBPPKOIYKANZWPLLVWMQJFGQYLL
FLSO


2. Hankson的趣味题
(son.pas/c/cpp)
【问题描述】
Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1,设某未知正整数x满足:
1、 x和a0的最大公约数是a1;
2、 x和b0的最小公倍数是b1。
Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。

【输入】
输入文件名为son.in。第一行为一个正整数n,表示有n组输入数据。接下来的n行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证a0能被a1整除,b1能被b0整除。
【输出】
输出文件son.out共n行。每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的x,请输出0;
若存在这样的x,请输出满足条件的x的个数;
【输出输出样例】
son.in          son.out
2
41 1 96 288          6
95 1 37 1776          2
【说明】
第一组输入数据,x可以是9、18、36、72、144、288,共有6个。
第二组输入数据,x可以是48、1776,共有2个。
【数据范围】
对于50%的数据,保证有1≤a0,a1,b0,b1≤10000且n≤100。
对于100%的数据,保证有1≤a0,a1,b0,b1≤2,000,000,000且n≤2000。


3.Mayan 游戏
(mayan.cpp/c/pas)
【问题描述】
Mayan puzzle 是最近流行起来的一个游戏。游戏界面是一个7 行5 列的棋盘,上面堆放
着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游
戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:
1、每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方
块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参
见输入输出样例说明中的图6 到图7);如果目标位置上没有方块,那么被拖动的方块将从
原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图1 和图2);

【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】

【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】

【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】


解题报告:

  第一题:如果把仔细审题,理解清楚,各种输出failed的条件分析清楚,再用映射map将其密文、译文对应,就没有问题。初次独立写map还是遇到了问题,先开始我定义的是string对应string,然后就报错了,后来改成char就可以了。然后写的时候把对应关系搞反了,应该是密文对应译文,不是译文对应密文。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
using namespace std;
string s1,s2,s3;
int len1,len2;
map <char,char> m;//改string为char
map <char,bool> canmi;
bool b=true,all[];
int main()
{
freopen("spy.in","r",stdin);
freopen("spy.out","w",stdout);
cin>>s1;len1=s1.size();
cin>>s2;
cin>>s3;len2=s3.size();
memset(all,false,sizeof(all));
for (int i=;i<len1;i++)
{
char x=s1[i],y=s2[i];//改string为char
if (!m[x]){ //没有对应密文
if (!canmi[x]){ //密文没有用过,有效
m[x]=y;
canmi[x]=true;
int t=y-;
all[t]=true;
}
else{
b=false;
} //密文用过 ,无效 (不能翻译)
}
else if (m[x]!=y){ //有对应密文且这次对应的与已知的不一样,无效
b=false;
}
if (!b) {
printf("Failed");
return ;
}
}
for (int i=;i<=;i++)
if (!all[i])
b=false;
if (b){
for (int i=;i<len2;i++)
{
char x=s3[i];
cout<<m[x];
}
}
else printf("Failed");
return ;
}

  第二题:数论问题,一定要在草稿纸上多算,学会假设列式子,解式子。

    解法一:因为gcd(x,a0)=a1,不妨设x=p*a1。
        所以lcm(x,b0)=lcm(p*a1,b0)=p*a1*b0/gcd(p*a1,b0)=b1
        可以得到p=b1* gcd(p*a1,b0)/a1/b0,枚举gcd(p*a1,b0)后可以求得p,继而求得x,检验gcd(p*a1,a0)是否为a1即可。
        因为gcd(p*a1,b0)是b1的约数,所以枚举量为O(sqrt(b1)),最后复杂度为O(N*sqrt(b1)*log(b1)),期望得分100.(表示只得了50,还要再改一下)       解法二:因为x和a0的最大公约数为a1,所以x/a1与a0/a1肯定互质,同样,x和b0的最小公倍数是b1,所以b1/x和b1/b0肯定互质。那么枚举b1 的约数,如果满足,那么可取。注意判断,如果a0%a1 || b1%b0 都除不尽,说明没有答案,同样,在枚举答案的时候,要x/a1,b1/x 除得尽才行。

    解法三:因为lcm(x,b0)=x*b0/gcd(x,b0)=b1,所以x/gcd(x,b0)=b1/b0。不妨把b1/b0分解质因数,又因为x有约数a1,结合素数表进行搜索也能快速出结果。效率不好分析,期望得分100。(这个做法还没有做)、

    下面给出解法一的50分:

 #include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
int n,ans;
int gcd(int a,int b)
{
return b==?a:gcd(b,a%b);
}
int doit(int a0,int a1,int b0,int b1)
{
int an=;
for (int i=;i;i++)
{
if (i*i>b1) break;
if (b1%i==)
{
int x=b1*i/b0,o=b1/i;
if (gcd(x,a0)==a1&&gcd(x,b0)==x*b0/b1) an++;
if (i!=o)
{
int y=b1*o/b0;
if (gcd(y,a0)==a1&&gcd(y,b0)==y*b0/b1) an++;
}
}
}
return an;
}
int main()
{
freopen("son.in","r",stdin);
freopen("son.out","w",stdout);
cin>>n;
for (int i=;i<=n;i++)
{
int a0,a1,b0,b1;
scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
int ans=doit(a0,a1,b0,b1);
printf("%d\n",ans);
}
return ;
}

    解法二的100分:

 #include<iostream>
#include<cstdio>
using namespace std;
int n;
int gcd(int a,int b)
{
return b==?a:gcd(b,a%b);
}
int main()
{
freopen("son.in","r",stdin);
freopen("son.out","w",stdout);
cin>>n;
for (int i=;i<=n;i++)
{
int a0,a1,b0,b1,ans=;
scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
if (a0%a1||b1%b0)
{
cout<<;
continue;
}
int t1=a0/a1,t2=b1/b0;
for (int j=;j*j<=b1;j++)
if (b1%j==)
{
if (j%a1==) {
if ((gcd(j/a1,t1)==)&&(gcd(b1/j,t2)==)) ans++;
}
int x=b1/j;
if (x!=j&&x%a1==){
if ((gcd(x/a1,t1)==)&&(gcd(b1/x,t2)==)) ans++;
}
}
printf("%d\n",ans);
}
return ;
}

    解法三的标程:

 #include<iostream>//数据全过。
#define MAXN 50000//x一定是b1的因子,找出b1的质因子及其个数
#define MAXP 6000//然后搜索b1的因子,进行判断,每组数据不超过0.2秒
#define MAXR 1000
using namespace std;
int p[MAXP],c[MAXR],d[MAXR],total,num,ans,a0,a1,b0,b1,t,n;
bool f[MAXN];
void init()
{
memset(f,,MAXN);
for (int i=;i<=MAXN;++i) if (f[i])
{
p[++total]=i;
for (int j=,lim=MAXN/i;j<=lim;++j) f[i*j]=false;
}
}
int gcd(int a,int b)
{
if (b==) return a;else return gcd(b,a%b);
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
void dfs(int i,int w)
{
if (i==num+)
{
if (gcd(w,a0)==a1&lcm(w,b0)==b1) ans++;
return;
}
for (int j=;j<=c[i];++j)
{
dfs(i+,w);
w*=d[i];
}
}
int main()
{
init();
freopen("son.in","r",stdin);freopen("son.out","w",stdout);
scanf("%d",&n);
while (n--)
{
scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
num=;ans=;
t=b1;
for (int i=;i<=total;++i) if (t%p[i]==)
{
c[++num]=;d[num]=p[i];
while (t%p[i]==)
{
t/=p[i];
++c[num];
}
if (t==) break;
}
if (t!=) { c[++num]=;d[num]=t;}
dfs(,);
printf("%d\n",ans);
}
return ;
}

  第三题:这道题是两个多月前做的一道复杂的题,涉及到许多函数,总体来讲是一个dfs,再加上几个剪枝:

  • 左右交换是等价的,根据题中的顺序,只需向右交换即可
  • 某个颜色方块的数量<=2,则很显然不能被消除

。虽然做过但是考试的时候并没有打算编这个,因为又忘了具体怎么做了。编好后,还是有一些问题:(1)一些细节的判断。(2)在复制相似的代码时,记得改不同的值(3)取变量的名称时最好不要取完整的单词,因为可能在某个库里有这个定义,导致你的定义不能用,比如这次取的count,在自己评测和VJ上评测都过了,在cena上编译错误,因为没有定义到。

  这种比较复杂的题,在考试的时候如果有时间一定要把它分为几段过程,一段一段地考虑,化整为零,这样就把题目简化了。一定要有耐心。

 #include<iostream>
#include<cstdio>
#include<cstdlib>//exit(0);
using namespace std;
struct nodem{
int mp[][];
};
nodem map;
struct nodec{
int c[];
};
nodec coun;
int n,ans[][];
bool no()
{
for (int i=;i<=;i++)
if (coun.c[i]==||coun.c[i]==) return ;
return ;
}
void down()
{
for (int i=;i<=;i++)
for (int j=;j<=;j++)
if (map.mp[i][j]>)
{
int k=j-;
while (k>&&map.mp[i][k]==)
{
map.mp[i][k]=map.mp[i][k+];
map.mp[i][k+]=;// k+1 不是 j
k--;
}
}
for (int i=;i<=;i++)
{
map.mp[i][]=;
for (int j=;j<=;j++)
if (map.mp[i][j]) map.mp[i][]++;
else break;
}
}
int clear()
{
nodem mm;
int flag=;
for (int i=;i<=;i++)
for (int j=;j<=;j++)
mm.mp[i][j]=;
for (int i=;i<=;i++)
for (int j=;j<=;j++)
if (map.mp[i][j]> /*!*/&&map.mp[i][j]==map.mp[i+][j]&&map.mp[i+][j]==map.mp[i+][j])
{
mm.mp[i][j]=;mm.mp[i+][j]=;mm.mp[i+][j]=;
}
for (int i=;i<=;i++)
for (int j=;j<=;j++)
if (map.mp[i][j]>/*!*/&&map.mp[i][j]==map.mp[i][j+]&&map.mp[i][j+]==map.mp[i][j+])
{
mm.mp[i][j]=;mm.mp[i][j+]=;mm.mp[i][j+]=;
}
for (int i=;i<=;i++)
for (int j=;j<=;j++)
if (mm.mp[i][j]==)
{
flag=;
coun.c[map.mp[i][j]]--;
map.mp[i][j]=;
}
return flag;
}
void doit()
{
down();
while(clear()) down();
}
void work(int x)
{
if (x==n+)
{
for (int i=;i<=;i++)
if (map.mp[i][]>) return ;// !
for (int i=;i<=n;i++)
printf("%d %d %d\n",ans[i][],ans[i][],ans[i][]);
exit();//在for循环外
}
if (no()) return ;
nodem m=map;
nodec cc=coun;
for (int i=;i<=;i++)
for (int j=;j<=map.mp[i][];j++)
{
if (i<)
{
int t=map.mp[i][j];map.mp[i][j]=map.mp[i+][j];map.mp[i+][j]=t;
ans[x][]=i-;ans[x][]=j-;ans[x][]=;
doit();
work(x+);
map=m;
coun=cc;
}
if (i>&&map.mp[i-][j]==)
{
int t=map.mp[i][j];map.mp[i][j]=map.mp[i-][j];map.mp[i-][j]=t;
ans[x][]=i-;ans[x][]=j-;ans[x][]=-;
doit();
work(x+);
map=m;
coun=cc;
}
}
}
int main()
{
freopen("mayan.in","r",stdin);
freopen("mayan.out","w",stdout);
cin>>n;
for (int i=;i<=;i++)
{
int h=,k;
scanf("%d",&k);
while (k!=)
{
map.mp[i][++h]=k;
coun.c[k]++;
scanf("%d",&k);
}
map.mp[i][]=h;
}
work();
printf("-1\n");
return ;
}

  总的来说,这次考试呢,提醒我一定要多复习以前做过的题,还有自己的薄弱项,比如数论,动态规划,图论等。刷题刷题。。。