min:
=f[ch];
end;
k:
=max-min;
ifjudge(k)then//输出
begin
writeln('LuckyWord');
write(k);
end
else
begin
writeln('NoAnswer');
write(0);
end;
fclose;
end.
2.火柴棒等式
预处理下,然后枚举、剪枝,范围稍微开大点,弄到2000似乎足够了,剪枝后不会超时的。
首先预处理下每个数(0~2000)需要多少个火柴棒,然后枚举A和B,再判断。
参考程序:
programmatches;
const
inp='matches.in';
oup='matches.out';
num:
array['0'..'9']ofinteger=(6,2,5,5,4,5,6,3,7,6);//0~9需要的火柴棒数
maxn=1000;
var
f:
array[0..maxn*2]oflongint;
i,j,k,n,ans:
longint;
s:
string;
procedureflink;
begin
assign(input,inp);
reset(input);
assign(output,oup);
rewrite(output);
end;
procedurefclose;
begin
close(input);
close(output);
end;
procedureinit;//预处理0~2000每个数需要的火柴棒数
var
i,j,k:
longint;
s:
string;
begin
fori:
=0tomaxn*2do
begin
str(i,s);
f[i]:
=0;
forj:
=1tolength(s)do
inc(f[i],num[s[j]]);
end;
end;
begin
flink;
readln(n);
init;
ans:
=0;
n:
=n-4;//总火柴棒数减去'+'和'='所需的火柴棒数
fori:
=0tomaxndo//枚举A
begin
iff[i]>=nthencontinue;//剪枝
forj:
=0tomaxndo//枚举B
begin
iff[i]+f[j]>=nthencontinue;//剪枝
k:
=i+j;
iff[i]+f[j]+f[k]=ntheninc(ans);//符合条件,总数加一
end;
end;
write(ans);
fclose;
end.
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',y1')为把纸条(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;
const
inp='message.in';
oup='message.out';
var
a:
array[1..50,1..50]oflongint;
f:
array[0..200,1..100,1..100]oflongint;
tmp,i,j,k,n,m,d,x1,x2,y1,y2:
longint;
procedureflink;
begin
assign(input,inp);
reset(input);
assign(output,oup);
rewrite(output);
end;
procedurefclose;
begin
close(input);
close(output);
end;
functionmax(a,b:
longint):
longint;
begin
ifa>bthenexit(a);
exit(b);
end;
begin
flink;
readln(n,m);
fori:
=1tondo
begin
forj:
=1tomdo
read(a[i,j]);
readln;
end;
fillchar(f,sizeof(f),0);
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:
=1tondo
begin
y1:
=(d+2)-x1;
if(y1<=0)or(y1>m)thencontinue;//排除不合法的x1
forx2:
=1tondo
begin
ifx1=x2thencontinue;//排除不合法的x1,x2
y2:
=(d+2)-x2;
if(y2<=0)or(y2>m)thencontinue;//排除不合法的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
tmp:
=max(tmp,f[d-1,x1-1,x2-1]);
//从(x1-1,y1)传到(x1,y1),(x2-1,y2)传到(x2,y2)
if(x1-1>0)and(x1-1<>x2)then
tmp:
=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
tmp:
=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];
end;
end;
write(f[n+m-3,n,n-1]);//终点的只可能从两个点来,所以终点状态前移一个阶段。
fclose;
end.
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)如果qii)如果t1=p,执行B,continue;
iii)如果qiv)如果q=t2,执行D,continue;
v)输出无解信息,halt;
这个算法应该是错误的。
一个反例是:
7251463。
在上述四个判断条件中,能够产生冲突的只有i和iii,在i和iii冲突的时候需要判断。
所以我对上述算法进行修改:
i)如果((qii)如果t1=p,执行B,continue;
iii)如果qiv)如果q=t2,执行D,continue;
v)输出无解信息,halt;
归纳法证明算法正确性:
当n很小的时候(至少我没举出反例)算法是正确的。
设n当q=k时,显然(不存在x,使得q不存在或者t2不存在。
只要最大的数k放在了栈底,对其他的数都是没有影响的。
所以算法依然正确。
证毕。
(表述的很不规范。
)
NOIP2008提高组解题报告
angwuy
1word
这道题完全是送分题,只需要直接统计,再判断素数。
参考程序:
var
st:
string;
max,min,i:
longint;
a:
array['a'..'z']oflongint;
ch:
char;
functionfun(n:
longint):
boolean;
vari:
longint;
begin
ifn<2thenbeginfun:
=false;exit;end;
fori:
=2ton-1do
ifnmodi=0thenbeginfun:
=false;exit;end;
fun:
=true;
end;
begin
assign(input,'word.in');
reset(input);
assign(output,'word.out');
rewrite(output);
readln(st);
fillchar(a,sizeof(a),0);
fori:
=1tolength(st)do
inc(a[st[i]]);
max:
=0;
min:
=101;
forch:
='a'to'z'do
ifa[ch]>0then
begin
ifa[ch]>maxthenmax:
=a[ch];
ifa[ch]=a[ch];
end;
iffun(max-min)then
begin
writeln('LuckyWord');
writeln(max-min);
end
else
begin
writeln('NoAnswer');
writeln(0);
end;
close(input);
close(Output);
end.
2matches
搜索题,由于输入的情况只有25种,所以打表也是一种可行的方法。
在数据最大时,经过人工和电脑证明是不会到达四位数的,所以可以直接用O(1000*1000)的搜索算法
参考程序:
const
mat:
array[0..9]oflongint=(6,2,5,5,4,5,6,3,7,6);
functionfun(m:
longint):
longint;
vart:
longint;
begin
t:
=0;
whilem>0do
begin
inc(t,mat[mmod10]);
m:
=mdiv10;
end;
fun:
=t;
end;
vara:
array[0..1000]oflongint;
n,i,j,ans:
longint;
begin
assign(input,'matches.in');
reset(input);
assign(output,'matches.out');
rewrite(output);
readln(n);
ifn<10thenbeginwriteln(0);close(output);exit;end;
a[0]:
=6;
fori:
=1to1000do
a[i]:
=fun(i);
dec(n,4);
fori:
=0to1000do
ifa[i]begin
forj:
=0to1000-ido
ifa[i]+a[j]+a[i+j]=ntheninc(ans);
end;
writeln(ans);
close(input);
close(output);
end.
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答案:
max(dp[m,j,n]+sum[m,j,n])
参考程序:
constmaxn=10;
var
a:
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:
longint;
functionmax(a,b:
longint):
longint;
begin
ifa>bthenmax:
=aelsemax:
=b;
end;
begin
assign(input,'message.in');
reset(input);
assign(output,'message.out');
rewrite(output);
readln(m,n);
fori:
=1tomdo
begin
forj:
=1tondo
read(a[i,j]);
readln;
end;
fori:
=1tomdo
begin
fori1:
=1tondo
begin
sum[i,i1,i1]:
=a[i,i1];
fori2:
=i1+1tondo
sum[i,i1,i2]:
=sum[i,i1,i2-1]+a[i,i2];
end;
end;
fillchar(dp,sizeof(dp),255);
fori:
=2tondo
dp[2,1,i]:
=sum[1,1,i];
fori:
=2tom-1do
forj:
=1ton-1do
fork:
=j+1tondo
ifdp[i,j,k]>-1then
begin
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]);
end;
k:
=0;
fori:
=1ton-1do
k:
=max(k,dp[m,i,n]+sum[m,i,n]);
writeln(k);
close(input);
close(output);
end.
第四题的解题报告都有很多大牛写了,那我就不在这里献丑了
好象是用二分图来做的,在OIBH上有详细的题解