整体分析
这次得了个400,不算大悲剧,但是时间却很慢,而且第一题调的时间太长了
1(chars)
题目大意
对于石台上的一串数字,你可以在适当的位置加入乘号(设加了k 个,当然也可不加,
即分成k+1 个部分),设这k+1 个部分的乘积(如果k=0,则乘积即为原数串的值)对m 的
余数(即mod m)为x;
现求x 能达到的最小值及该情况下k 的最小值,以及x 能达到的最大值及该情况下的k
的最小值(可以存在x 的最小值与最大值相同的情况)。
用到的数据结构,算法
动态规划,数学知识,同余定理
正确分析
这个题和乘积最大拿到非常相似,我们先回顾一下这个经典模型的方程f[i,j]表示前i个字符,添加j个乘号能够得到的最大值,显然f[i,j]=max{f[k,j-1]*num[k+1,i]}(0<k<=i-1)
那么这道题设计到了余数,就出现了后效性问题,所以我们换一种方程的表示方法,f[i,j]表示前i个字符得到j的余数需要的最少的变换次数(题目要求最优)。于是我们得到了如下的方程f[i,k*num[j,i] mod m]=min{f[j,k]}+1;其中k枚举余数从0~m-1
特别的这道题需要数学知识,同余定理,对于num数组我们可以做预处理。只保留其数值对m的余数,这样下来是不会影响结果的。那么我们会得到num的递推关系(详见代码)
这道题还需要注意下细节
错误原因
没有找到num的递推关系,同余定理掌握不好,没有将细节,边界等处理好
反思
经典问题的变形,但是方法却截然不同,虽然改变了方程的表示,但是决策选择的思想是一致的,所以要特别加深对经典模型的理解,一遍能很快写出方程
这里输出答案时减一,完全是因为方程含义,和初值的原因
如何输出答案时不减一,那么赋初值时,就应该用循环
for i:=1 to n do f[i,s[1,i] mod m]:=0;
总之,要把该赋的显然情况都赋上,而且要保证能够推出后面的状态
program liukee;
var
f:array[0..1000,0..100] of longint;
s:array[0..1000,0..1000] of longint;
a:array[0..1000] of longint;
n,m,i,j,k:longint;
function min(a,b:longint):longint;
begin
if a<b then exit(a);
exit(b);
end;
procedure init;
var
i,j:longint;
ch:char;
begin
read(ch);
while not eoln do
begin
if ch in ['1'..'9'] then
begin
inc(n);
a[n]:=ord(ch)-48;
end;
read(ch);
end;
if ch in ['1'..'9'] then
begin
inc(n);
a[n]:=ord(ch)-48;
end;
readln(m);
for i:=1 to n do
for j:=i to n do
s[i,j]:=(s[i,j-1]*10+a[j] mod m)mod m;
end;
begin
assign(input,'chars.in');reset(input);
assign(output,'chars.out');rewrite(output);
init;
filldword(f,sizeof(f)>>2,maxlongint>>1);
f[0,1]:=0;
for i:=1 to n do
for j:=1 to i do
for k:=0 to m-1 do
if f[j-1,k]<>maxlongint>>1 then
f[i,(k*s[j,i]) mod m]:=min(f[i,k*s[j,i] mod m],f[j-1,k]+1);
for i:=0 to m-1 do
if f[n,i]<>maxlongint>>1 then
begin
write(i,' ',f[n,i]-1,' ');
break;
end;
for i:=m-1 downto 0 do
if f[n,i]<>maxlongint>>1 then
begin
write(i,' ',f[n,i]-1);
break;
end;
close(input);
close(output);
end.
2(最接近神的人)
题目大意
破解了符文之语,小FF 开启了通往地下的道路。当他走到最底层时, 发现正前方
有一扇巨石门, 门上雕刻着一副古代人进行某种活动的图案。而石门上方用古代文写着“神
的殿堂“。小FF 猜想里面应该就有王室的遗产了。但现在的问题是如何打开这扇门……
仔细研究后, 他发现门上的图案大概是说:古代人认为只有智者才是最容易接近神明
的。而最聪明的人往往通过一种仪式选拔出来。仪式大概是指,即将隐退的智者为他的候
选人写下一串无序的数字, 并让他们进行一种操作, 即交换序列中相邻的两个元素。而
用最少的交换次数使原序列变成不下降序列的人即是下一任智者。
小FF 发现门上同样有着n 个数字。于是他认为打开这扇门的秘诀就是找到让这个序
列变成不下降序列所需要的最小次数。但小FF 不会……只好又找到了你,并答应事成之后
与你三七分……
【输入格式】
第一行为一个整数n,表示序列长度
第二行为n 个整数,表示序列中每个元素。
【输出格式】
一个整数ans,即最少操作次数。
用到的数据结构,算法
归并排序
正确分析
假设a b 相邻,若a<b 交换会增加逆序数,所以最好不要做此交换;若a=b 交
换无意义,也不要进行此交换;a>b 时,交换会减少逆序,使序列更顺序,所以做
交换。
由上可知,所谓的移动只有一种情况,即a>b,且一次移动的结果是逆序减1。
假设初始逆序是n,每次移动减1,那么就需要n 次移动时序列变为顺序。所以题目
转化为直接求序列的逆序便可以了。
具体求的方法不再赘言
注意ans要开qword逆序对数可能远大于序列长度
错误原因
归并排序时,少打了两句
temp1[l1+1]:=maxlongint;
temp2[l2+1]:=maxlongint;
反思
能透过表面现象看到本质,如果无法证明,也要先打上,然后手工数据去验证。
对于基础代码一定要熟练,不能出现一点错误。
program liukee;
var
a:array[1..500001] of longint;
temp1,temp2:array[1..500000] of longint;
n,i:longint;
ans:qword;
procedure merge(l,r:longint);
var
mid,i,j,l1,l2,k:longint;
begin
mid:=(l+r)>>1;
if l<mid then merge(l,mid);
if r>mid+1 then merge(mid+1,r);
l1:=mid-l+1;
l2:=r-mid;
for i:=1 to l1 do
temp1[i]:=a[l+i-1];
for i:=1 to l2 do
temp2[i]:=a[mid+i];
temp1[l1+1]:=maxlongint;
temp2[l2+1]:=maxlongint;
i:=1;
j:=1;
for k:=l to r do
if temp1[i]<=temp2[j] then
begin
a[k]:=temp1[i];
inc(i);
end
else
begin
a[k]:=temp2[j];
inc(j);
inc(ans,l1-i+1);
end;
end;
begin
assign(input,'sophist.in');reset(input);
assign(output,'sophist.out');rewrite(output);
readln(n);
ans:=0;
for i:=1 to n do
read(a[i]);
merge(1,n);
writeln(ans);
close(input);
close(output);
end.
3(古代人的难题)
题目大意
已知x, y 为整数,且满足以下两个条件:
1. x, y ϵ [1..k], 且x,y,k ϵ Z;
2. (x^2 – xy – y^2)^2 = 1
给你一个整数k, 求一组满足上述条件的x, y 并且使得x^2 + y^2 的值最大。
用到的数据结构,算法
斐波那契数列
正确分析
很明显这是一个数学题, 其实这是个Fibomacci 数列模型, 下面我们来证明下:
(x^2 - xy - y^2)^2
= (y^2 + xy - x^2)^2
= [(x+y)^2 - xy - 2*x^2]^2
=[(x+y)^2 - (x+y)*x - x^2]^2
由上式可知, 如果x, y 满足条件2, 那么x+y, y 也满足条件2。
那么Fibomacci 中小于等于k 的最大两个相邻的数即为试题所需的解。
错误原因
方向正确,推导不彻底,没有观察出关系
反思
有些数学题,很难写出通项公式,但是可以去推导一些递推式,或者是规律行的东西。要善于用已有知识,边化简边观察,将能想到的数学知识都试试。
这个题打表就可以
如果数据范围太大或者递推关系复杂,可以考虑用构造矩阵的方法
4(宝物筛选)
题目大意
多重背包,每个物品有限件
用到的数据结构,算法
动态规划,二进制拆分,转化为01背包求解
正确分析
略
错误原因
无
反思
熟练掌握
program liukee;
var
v,w:array[1..100000] of longint;
f:array[0..100000] of longint;
i,j,n,vtot,tt:longint;
procedure init;
var
x,y,z,i,k:longint;
begin
readln(n,vtot);
for k:=1 to n do
begin
readln(y,x,z);
i:=1;
while z>i do
begin
dec(z,i);
inc(tt);
v[tt]:=x*i;
w[tt]:=y*i;
i:=i*2;
end;
inc(tt);
v[tt]:=z*x;
w[tt]:=z*y;
end;
end;
function max(a,b:longint):longint;
begin
if a>b then exit(a);
exit(b);
end;
begin
assign(input,'treasure.in');reset(input);
assign(output,'treasure.out');rewrite(output);
init;
for i:=1 to tt do
for j:=vtot downto v[i] do
f[j]:=max(f[j-v[i]]+w[i],f[j]);
writeln(f[vtot]);
close(input);
close(output);
end.