NOIP提高组前三题解题报告文档格式.docx
- 文档编号:17857295
- 上传时间:2022-12-11
- 格式:DOCX
- 页数:14
- 大小:20.38KB
NOIP提高组前三题解题报告文档格式.docx
《NOIP提高组前三题解题报告文档格式.docx》由会员分享,可在线阅读,更多相关《NOIP提高组前三题解题报告文档格式.docx(14页珍藏版)》请在冰豆网上搜索。
procedurefclose;
close(input);
close(output);
functionjudge(k:
longint):
boolean;
//判断素数,需考虑0和1的情况
var
i:
if(k=0)or(k=1)thenexit(false);
fori:
=2totrunc(sqrt(k))do
ifkmodi=0thenexit(false);
exit(true);
begin
flink;
readln(s);
fillchar(f,sizeof(f),0);
=1tolength(s)do//统计每个字符出现的次数
inc(f[s[i]]);
min:
=1000;
max:
=0;
forch:
='
to'
do//统计最大和最小
if(f[ch]<
>
0)and(f[ch]>
max)then
=f[ch];
0)and(f[ch]<
min)then
k:
=max-min;
ifjudge(k)then//输出
writeln('
LuckyWord'
);
write(k);
end
else
NoAnswer'
write(0);
fclose;
end.
2.火柴棒等式
预处理下,然后枚举、剪枝,范围稍微开大点,弄到2000似乎足够了,剪枝后不会超时的。
首先预处理下每个数(0~2000)需要多少个火柴棒,然后枚举A和B,再判断。
programmatches;
matches.in'
matches.out'
num:
0'
9'
]ofinteger=(6,2,5,5,4,5,6,3,7,6);
//0~9需要的火柴棒数
maxn=1000;
array[0..maxn*2]oflongint;
i,j,k,n,ans:
procedureinit;
//预处理0~2000每个数需要的火柴棒数
i,j,k:
=0tomaxn*2do
str(i,s);
f[i]:
forj:
=1tolength(s)do
inc(f[i],num[s[j]]);
readln(n);
init;
ans:
n:
=n-4;
//总火柴棒数减去'
+'
和'
='
所需的火柴棒数
=0tomaxndo//枚举A
iff[i]>
=nthencontinue;
//剪枝
=0tomaxndo//枚举B
iff[i]+f[j]>
=i+j;
iff[i]+f[j]+f[k]=ntheninc(ans);
//符合条件,总数加一
write(ans);
3.传纸条
从A传给B,再从B传给A,中间路径不相交与某个人……。
换种思路,它完全等价于,从A点传出2张纸条到B,中间不能经过同一人。
这么看来似乎就是vijos三取方格数的简化版了。
解题思想就是双进程的动态规划。
阶段是按传递次数划分的。
F(d,x1,y1,x2,y2)表示第d次传递到坐标为(x1,y1),(x2,y2)两个人手中是得到的最大好心度。
那么可以列出方程:
F(d,x1,y1,x2,y2)=max{f(d-1,x1'
y1'
x2'
y2'
)+a[x1,y1]+a[x2,y2]|(x1'
)为把纸条(x1,y1)的人,(x2'
y2'
)为把纸条转给(x2,y2)的人}
(数学不太好,方程列的不专业,见谅^_^)
这么看来时间复杂度是O(maxd*n*m*n*m),其中maxd=n+m-2。
相当于50^5=312500000,超时是没得说的了。
其实我们可以把状态再压缩下,先来看下其中的规律:
起点(1,1)
第1次传递可到达(1,2)(2,1)
第2次传递可到达(1,3)(2,2)(3,1)
……
很快就可以发展其中的规律
第d次传递可到达的坐标(xi,yi)和d之间存在d+2=xi+yi
那么我们就可以把上面的状态转移方程转化成
f(d,x1,x2)=max{f(d-1,x1'
x2'
)+a[x1,(d+2-x1)]+a[x2,(d+2-x2)]|x1'
x2'
合法}
programmessage;
message.in'
message.out'
a:
array[1..50,1..50]oflongint;
array[0..200,1..100,1..100]oflongint;
tmp,i,j,k,n,m,d,x1,x2,y1,y2:
functionmax(a,b:
ifa>
bthenexit(a);
exit(b);
readln(n,m);
=1tondo
=1tomdo
read(a[i,j]);
readln;
f[0,1,1]:
=a[1,1];
f[1,1,2]:
=a[2,1]+a[1,2];
f[1,2,1]:
=a[1,2]+a[2,1];
//边界,因为中间需要判断点不重合,所以把必须重合的状态单独考虑
ford:
=2to(n+m-3)do
forx1:
y1:
=(d+2)-x1;
if(y1<
=0)or(y1>
m)thencontinue;
//排除不合法的x1
forx2:
ifx1=x2thencontinue;
//排除不合法的x1,x2
y2:
=(d+2)-x2;
if(y2<
=0)or(y2>
//排除不合法的x2
tmp:
=f[d-1,x1,x2];
//各自从上方取,即从(x1,y1-1)传到(x1,y1),(x2,y2-1)传到(x2,y2)
if(x1-1>
0)and(x2-1>
0)then
=max(tmp,f[d-1,x1-1,x2-1]);
//从(x1-1,y1)传到(x1,y1),(x2-1,y2)传到(x2,y2)
0)and(x1-1<
x2)then
=max(tmp,f[d-1,x1-1,x2]);
//从(x1-1,y1)传到(x1,y1),(x2,y2-1)传到(x2,y2)
if(x2-1>
0)and(x1<
x2-1)then
=max(tmp,f[d-1,x1,x2-1]);
//从(x1,y1-1)传到(x1,y1),(x2-1,y2)传到(x2,y2)
f[d,x1,x2]:
=tmp+a[x1,y1]+a[x2,y2];
write(f[n+m-3,n,n-1]);
//终点的只可能从两个点来,所以终点状态前移一个阶段。
4.双栈排序
难……,写不出满分程序,所以还是不写了……
NOIP2008提高组复赛解题思路
1、字符串中统计字母出现次数最大减最小的然后判断质数字符串长度<
=100
2、给出n<
=24个火柴棍,求最多能摆出多少a+b=c形式的等式。
数字的摆法和计算器相同,a,b,c>
=0
3、双进程方格取数:
m*n的棋盘,m,n<
=50,不需使用高精度
4、给出1..n的排列,两个栈S1、S2,入栈出栈共4个操作:
(n<
=1000)
A:
输入队列头入S1
B:
弹出S1栈顶元素,进入输出队列
C:
输入队列头入S2
D:
弹出S2栈顶元素,进入输出队列
要求将1..n的排列排序,采用字典序最小的操作方法
个人感觉是,单就难度来看,奥赛历史最简单,由于类似2,3题的模型大多数能拿一等的同学们都见过。
我的想法:
1、水题,最多40分钟搞定
2、如果是考试的话,10000*10000的枚举,差不多写程序+跑+打表=10+3+2min就够了吧(不过我用的是骗
分手段,一个一个手算。
然后IF-THEN,IF-THEN,最后15分钟搞定)。
3、经典题目,斜向划分阶段。
35分钟搞定(前三个题目1小时思路+编程+检查够了)
4、本届唯一挑战性题目。
考虑到要求字典序最小,那么按照ABCD的顺序贪心即可。
对于当前的状态,设该进入到输出序列中的是p,输入队列头是q,S1栈顶是t1,S2栈顶是t2,那么依次判
断,容易知道q>
=p
i)如果q<
t1或者t1不存在,执行A,continue;
ii)如果t1=p,执行B,continue;
iii)如果q<
t2或者t2不存在,执行C,continue;
iv)如果q=t2,执行D,continue;
v)输出无解信息,halt;
这个算法应该是错误的。
一个反例是:
7251463。
在上述四个判断条件中,能够产生冲突的只有i和iii,在i和iii冲突的时候需要判断。
所以我对上述算法进行修改:
i)如果((q<
t1或者t1不存在)and(输入队列中不存在x,使得q<
t2<
x<
t1)),执行A,continue;
归纳法证明算法正确性:
当n很小的时候(至少我没举出反例)算法是正确的。
设n<
k成立,考虑n=k的情况。
当q=k时,显然(不存在x,使得q<
t1),也没有q<
t1或者q<
t2的情况,所以k能够入栈的充要条件是t1
不存在或者t2不存在。
只要最大的数k放在了栈底,对其他的数都是没有影响的。
所以算法依然正确。
证毕。
(表述的很不规范。
)
NOIP2008提高组解题报告
angwuy
1word
这道题完全是送分题,只需要直接统计,再判断素数。
st:
max,min,i:
]oflongint;
functionfun(n:
vari:
ifn<
2thenbeginfun:
=false;
exit;
end;
=2ton-1do
ifnmodi=0thenbeginfun:
fun:
=true;
assign(input,'
assign(output,'
readln(st);
fillchar(a,sizeof(a),0);
=1tolength(st)do
inc(a[st[i]]);
=101;
do
ifa[ch]>
0then
maxthenmax:
=a[ch];
ifa[ch]<
minthenmin:
iffun(max-min)then
writeln(max-min);
writeln(0);
close(Output);
2matches
搜索题,由于输入的情况只有25种,所以打表也是一种可行的方法。
在数据最大时,经过人工和电脑证明是不会到达四位数的,所以可以直接用O(1000*1000)的搜索算法
参考程序:
mat:
array[0..9]oflongint=(6,2,5,5,4,5,6,3,7,6);
functionfun(m:
vart:
t:
whilem>
0do
inc(t,mat[mmod10]);
m:
=mdiv10;
=t;
vara:
array[0..1000]oflongint;
n,i,j,ans:
10thenbeginwriteln(0);
close(output);
a[0]:
=6;
=1to1000do
a[i]:
=fun(i);
dec(n,4);
=0to1000do
ifa[i]<
nthen
=0to1000-ido
ifa[i]+a[j]+a[i+j]=ntheninc(ans);
writeln(ans);
3message
DP题,两条路线必定一上一下,而且,当到达某一列后,前面对后面的不会有影响,符合动态规划的无后效性,方程如下:
用dp[I,j,k]表示当到达I列时,上路线在j行到,下路线在k行到的最大值。
另外加一个预处理,sum[I,j1,j2]表示在第I列j1到j2行的数加起来的和。
边界条件:
dp[2,1,k]:
=sum[1,1,k];
递推方程:
dp[I,j,k]:
=max(dp[I-1,j2,k2]+sum[I-1,j,j2]+sum[I-1,k,k2]){j<
=j2<
k<
=k2}
答案:
max(dp[m,j,n]+sum[m,j,n])
constmaxn=10;
array[1..maxn,1..maxn]oflongint;
dp,sum:
array[1..maxn,1..maxn,1..maxn]oflongint;
n,m,i,j,k,i1,i2,j1,j2,k1,k2:
bthenmax:
=aelsemax:
=b;
readln(m,n);
=1tomdo
=1tondo
fori1:
sum[i,i1,i1]:
=a[i,i1];
fori2:
=i1+1tondo
sum[i,i1,i2]:
=sum[i,i1,i2-1]+a[i,i2];
fillchar(dp,sizeof(dp),255);
=2tondo
dp[2,1,i]:
=sum[1,1,i];
=2tom-1do
=1ton-1do
fork:
=j+1tondo
ifdp[i,j,k]>
-1then
forj2:
=jtok-1do
fork2:
=ktondo
dp[i+1,j2,k2]:
=max(dp[i+1,j2,k2],dp[i,j,k]+sum[i,j,j2]+sum[i,k,k2]);
=max(k,dp[m,i,n]+sum[m,i,n]);
writeln(k);
第四题的解题报告都有很多大牛写了,那我就不在这里献丑了
好象是用二分图来做的,在OIBH上有详细的题解
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- NOIP 提高 组前三 题解 报告