11月8日的测试
整体分析
这次得了个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.