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.

 

 

posted @ 2010-11-08 14:33  liukee  阅读(331)  评论(1编辑  收藏  举报