【测试】11月11日的测试

整体分析

两道动规,一道基础算法,一道搜索。比较接近noip的模式,小分拿到了,就是第三题很可惜,方程推对了却没拿到分,总之,还是想的少,不专注,应该集中精力于当前的问题,既要套用经典,又要创新。

1(彩色穿孔卡片)

题目大意

问题描述

“在逃离诺莫瑞根的时候,我们留下了太多的数据!非常重要的数据!”大机械师卡斯派普十分着急地说,“尽管我们已经从矩阵打孔计算机上拿回了许多彩色穿孔卡片,但是混乱的数据令人无法忍受!”

自从诺莫瑞根陷落以后,侏儒们一直寄居在铁炉堡中。大机械师卡斯派普花了不少钱来悬赏勇士们去诺莫瑞根替他取回一些卡片,现在他已经有了一大堆彩色穿孔卡片。但是这些卡片都是残缺不全的,有的甚至还是无效的,想从这些破烂中恢复数据,实在是一件不容易的事。卡斯派普发现每个卡片的开头和结尾都有标记,记录着它原本在矩阵打孔计算机中序列的位置,于是想出了一个恢复数据的方法。把每张卡片看成数轴上的一条线段,开头和结尾的标记A,B为数轴上的两个点。卡斯派普按拿到的顺序把卡片一张一张地贴到数轴上,每张卡片的颜色都不同。他想知道贴完卡片以后的数轴上一共有多少种不同的颜色。卡斯派普请你帮助他写一个程序来解决这个问题。

输入格式

l 第1行:一个整数N,表示卡斯派普收集到的卡片的数量。

l 第2行至第N+1行:第i+1行给出了第i张卡片的头尾两个标记Ai,Bi,贴卡片的顺序与输入文件中出现的先后顺序一致。

输出格式

l 一个整数,表示卡斯派普能在数轴上看到的不同的颜色的数目。

样例输入

4

0 5

3 8

5 6

4 7

样例输出

3

 

用到的数据结构,算法

浮水法

正确分析

一开始想用类似校门外的树的方法解决,貌似没打对。现在有一种极其巧妙的浮水法。

对于一条线段,和它之后出现的线段依次比较,如果两线段不重叠那么直接穿透。如果重合,让能够穿透的部分继续穿透下层的线段,如果这样递归操作后能穿透所有的线段,那么我们就能得到一种新的颜色,这就好比从下浮到了水面上,就是一个深搜的过过程,很好理解,很形象,很简洁。

特别的我们要开一个数组纪录第i条线段是否浮出过水面,避免同一线段被累计两次。

错误原因

贪心策略不是很优秀,而且程序有问题

反思

浮水法不但可以应用于线段,还可以应用于面积,矩形的覆盖,只需要改一下约束条件即可。

像这种经典的想法要积累,而且能将问题抽象成实际模型。 

program liukee;
var 
  x,y:array[0..10000] of longint; 
  v:array[0..100000] of boolean;
  n,i,ans:longint;
  
procedure cut(xx,yy,t:longint);
begin
  if v[i] then exit;
  while(t<=n)and((yy<=x[t])or(xx>=y[t])) do inc(t);
  if t>n then
  begin
    inc(ans);
    v[i]:=true;
  end;
  if(xx<x[t])and(yy>x[t]) then cut(xx,x[t],t+1);
  if(xx<y[t])and(yy>y[t]) then cut(y[t],yy,t+1);
end;
  
begin
  assign(input,'punch.in');reset(input);
assign(output,'punch.out');rewrite(output);
  readln(n);
  ans:=1;
  for i:=1 to n do
    readln(x[i],y[i]);
  for i:=n-1 downto 1 do
    cut(x[i],y[i],i+1);
  writeln(ans);
  close(input);close(output);
end.
	

2(略)//原谅我的懒惰……

题目大意

用到的数据结构,算法

正确分析

错误原因

反思

3(阿鲁高的阴谋)

题目大意

在第三次战争中,达拉然被燃烧军团彻底催化。曾经是肯瑞托的一名成员的阿鲁高,在不断的失败和恐惧中,召唤了远古的恶魔——贪婪而又凶猛的沃根。沃根恐怖的力量很快清理了附近的亡灵天灾,但是它也开始对肯瑞托发起了攻击。可悲的是,阿鲁高也堕入了恶魔力量的深渊,他把附了沃根魔法的手腕强加在自己的朋友手上,让他们变成了奴仆。一个夜晚,阿鲁高带领沃根悄悄穿越格雷迈恩之墙,进攻了影牙城堡。原来辉煌的城堡,顷刻间被阴影和血腥味所笼罩。隐身于城堡内高高的塔顶不断扩充自己的军队,幻想有一天可以成为这片大陆的主人。终于有一天,巫妖王剥夺了阿鲁高死亡的权利,把阿鲁高变成了他当年痛恨的敌人,亡灵天灾。在诺森德,阿鲁高建立了血月神教,领导狼人们成为了天灾军团的爪牙。在灰陵的海岸上,阿鲁高建立了第一个据点,并让手下变成人形骗取了联盟军团的信任。

银溪镇的追随者们为阿鲁高制造了一个船,用于在海岸上扩张。在漫长的旅途中,阿鲁高必须消耗法力创造结界以存储燃料。在途中每一天,阿鲁高都可以驶到一个联盟港口骗取燃料,代价是消耗法力维持幻象。阿鲁高的目标是寒冰皇冠,要航行T天才能到达。由于天气和海域的不同,每天航行所需的燃料消耗也不同。每天都可以到达一个联盟港口,骗取一些燃料(也可以不去,从结界中取得燃料补充所需,注意,每天的消耗必须被满足),骗取每个单位燃料需要一定的法力消耗。如果骗取的燃料除供这一天消耗外还有剩余,则必须把它存到法力结界中,但是限于结界规模,只能把不超过V的燃料存储到结界中。在结界中存储的每个单位的燃料每天会消耗掉阿鲁高的W点法力值。为了留有足够多的法力,阿鲁高必须尽量地减少法力消耗。请你算出阿鲁高到达目标最少的法力消耗是多少。

输入格式

l 第1行,四个整数,航行的天数T,结界的最大存储量V,每个港口的库存A,结界中每存储一单位燃料一天的法力消耗W。

l 第2行至第1+T行,第k+1行有两个整数,分别表示第k天的需要的燃料N[k],抢夺第k天到达的港口的每个单位燃料的法力消耗B[k]。

输出格式

l 第1行,最小的法力消耗。

l 第2行至第1+T行,第k+1行有一个整数,表示第k天到达港口后抢夺燃料的数量。如果有多种解,使每天抢夺的燃料数组成一个序列,所有这些序列构成一个集合,对这个集合进行第一位为第一关键字,第二位为第二关键字,……的多关键字排序,输出其中最小的一个即可。

样例输入

2 5 10 5

5 20

3 30

样例输出

175

8

0

样例说明

在第1天骗取8个单位燃料,法力消耗8*20=160。5个用于当天直接使用,3个放入结界,存储法力消耗3*5=15。在第2天不需骗取,直接使用结界中的3个单位燃料。所以总法力消耗为160+15=175。

数据规模

1

<=T<=1000

1<=V,A,W,B[i]<=100

1<=N[i]<=V

用到的数据结构,算法

动态规划

正确分析

方程可根据数据范围确定

f[i,j]表示i天还剩(库存)j燃料。

f[i,j]:=max{f[i-1,j+cost[i]-k]+w*(j+cost[i]-k)+b[i]*k}k表示当天抢了多少

输出的时候有两种方式,只需要根据题目方程中的递归定义,用一个数组纪录决策,递归输出即可。

错误原因

方程正确,细节不对(j+cost[i]-k 范围的限定),初始化问题。输出方法没找到

反思

方程能推出来却得不到分是最悲剧的。注意初始化,注意细节,注意决策,状态的可行性。注意一切细节。

动规的输出方案不是无规律可循的,一般方法都是用数组纪录决策,然后递归过程输出即可(根据方程的含义,决策与变量的关系)。

program liukee;
var
  t,v,a,w:longint;
  f,path:array[0..1000,0..100] of longint;
  ans,cost,b:array[0..1000] of longint;
  i,j,k,an,vv,tt:longint;
  
procedure outit(x,y:longint);
begin
  if x=0 then exit;
  outit(x-1,y+cost[x]-path[x,y]);
  writeln(path[x,y]);
end;

begin
  assign(input,'arugal.in');reset(input);
  assign(output,'arugal.out');rewrite(output);
  readln(t,v,a,w);
  for i:=1 to t do
    readln(cost[i],b[i]);
  filldword(f,sizeof(f)>>2,maxlongint>>1);
  f[0,0]:=0;
  for i:=1 to t do
    for j:=0 to v do
	  for k:=0 to a do
	  begin
	    tt:=j+cost[i]-k;
		if (tt>=0)and(tt<=v) then
		  if f[i-1,tt]+tt*w+k*b[i]<f[i,j] then
		  begin
		    f[i,j]:=f[i-1,tt]+tt*w+k*b[i];
			path[i,j]:=k;
		  end;
	  end;
  writeln(f[t,0]);
{ for i:=t downto 1 do//这两种方法都可以,想想为什么
  begin
    ans[i]:=path[i,vv];
	vv:=vv+cost[i]-ans[i];
  end;
  for i:=1 to t do
    writeln(ans[i]);}
  outit(t,0);
  close(input);close(output);
end.


4(潜入辛迪加)

题目大意

“我们最新的研究成果《毒药研究方案》被可恶的辛迪加间谍偷走了!”作为拉文霍德的一员,你一定感到很震惊,因为它是我们最尖端的科研人员的一年的研究成果。被辛迪加获得,我们可能会有灭顶之灾。狡猾的辛迪加为了躲避我们的追杀,他们并没有把《毒药研究方案》带回激流堡,而是藏了起来。但是终究是我们技高一筹,通过购买侏儒的最新研究成果“静电放射探测器”,我们已经发现了他们的藏身之地。原来他们早就在奥特兰克山脉的地下修建了一个巨大的城市,现在,他们就把《毒药研究方案》放在了城市的最深处。更好的消息是,我们已经发现了地下城的入口。作为一名出色的盗贼,你要学会以彼之道,还施彼身——把《毒药研究方案》偷回来。然而辛迪加布置了严密的防御,更糟糕的是,他们从地精购买了电磁监视器。无论你的潜行技巧有多么高明,只要一接近它,就会出发警报。只有破坏它的供电系统,才能电磁监视器悄无声息得失效。

现在,“静电放射探测器”已经为我们生成了一张地图,它可以告诉你整个地下城的布局结构,包括每一个电磁监视器的位置,及其供电装置的位置。辛迪加的地下城可以被描述为一个N*N的表格,城市的入口在(1,1)处,目标《毒药研究方案》在(N,N)处。每个单元格可能是一片空地、一个障碍物、一个辛迪加卫士、一个电磁监视器、或者一个的供电装置。从入口处开始,每步你只能向上、下、左、右移动到相邻的一个单元格,不可以停留在原地。你只能进入空地,或者失去供电系统的电磁监视器的位置,或者摧毁供电装置。你不能移动到障碍物上,也不能进入辛迪加卫士的视线中。辛迪加卫士可以监视自己所在单元格以及上下左右共五格的位置,而且他们的视线可以重叠。你不能杀死辛迪加卫士,也不能被他们发现。每个电磁监视器的供电装置可能存在,也可能无法破坏或者根本不存在。一个供电装置也可能会对应零个、一个或多个电磁监视器,意味着摧毁它,对应的所有电磁监视器都会失效。(1,1)和(N,N)一定是可以通行的。拉文霍德要求你在执行任务之前首先给出一个计划书,即要求算出至少一共需要多少步,才能拿到我们的《毒药研究方案》。

输入格式

第1行,两个整数N, M。表示地图大小为N*N,供电装置的数量为M。

第2-N+1行,每行N个整数,每个整数i可能是0,-1,-2或者一个正整数。i=0表示该位置为一块空地,i=-1表示该位置为一个障碍物,i=-2表示该位置为一个辛迪加卫士。如果i是一个属于[1,M]的正整数,则表示该位置为一个供电装置,其编号为i。如果i是一个大于M的正整数,则表示该位置为一个电磁监视器,它的电力由编号为i-M的供电装置提供。

输出格式

一个整数,为拿到《毒药研究方案》所需的最少的步数。

样例输入

6 2

0 0 0 -2 -1 2

-1 0 0 0 -1 0

-2 0 0 0 3 3

-2 0 0 -1 -1 4

0 -1 0 0 -1 0

1 0 0 0 -1 0

样例输出

24

样例说明

地图如下图,S为入口,T为目标,黑色的单元格为障碍物。每个E表示一个卫兵,(E)为卫兵的监视范围。K1表示供电装置1,K2表示供电装置2。D1表示供电装置为1的电磁监视器,D2表示供电装置为2的电磁监视器。

最优的路线为(1,1) →(1,2) →(2,2) →(2,3) →(3,3) →(4,3) →(5,3) →(6,3) →(6,2) →(6,1)(破坏供电1) →(6,2) →(6,3) →(5,3) →(4,3) →(3,3) →(3,4) →(3,5) →(3,6) →(2,6) →(1,6)(破坏供电2) →(2,6) →(3,6) →(4.6) →(5,6) →(6,6)

用到的数据结构,算法

宽度优先搜索,哈希表。

正确分析

这看上去就是一道宽搜,但是其实是加深难度的,多层宽搜。

我们按照宽搜的方式做,但是不能说走过的点不再走,那么如何判重——哈希,每个点和当前对应的状态(二进制串,表示供电装置是否被摧毁)。每次入队操作前,只需要根据当前的状态(队列元素多开一维),和队首状态比较,决定下一步搜索,即可。相当于将上次的图抹掉,重新构图搜索。

错误原因

没有想到枚举状态的思想

反思

碰到复杂的搜索,要学会简化问题,将状态变成纪录存入队列,来实现多重宽搜。但是其本质是不变的。二进制数是表示状态的好东西。

const max=3000000;
type lkj=record
      x:longint;
	  y:longint;
	  z:longint;
	 used:longint;
	 end;
var
  a:array[0..50,0..50] of longint;
  d:array[1..4,1..2] of longint=((1,0),(-1,0),(0,1),(0,-1));
  get:array[1..16] of longint=(1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768);
  hash:array[0..50,0..50,0..65536] of boolean;
  q:array[0..3000000] of lkj;
  l,r,i,j,xx,yy,now,n,m:longint;
procedure init;
var
  i,j,k:longint;
begin
  readln(n,m);
  for i:=1 to n do
  begin
    for j:=1 to n do
	begin
      read(k);
	  if a[i,j]<>-1 then a[i,j]:=k;
	  if k=-2 then
	  begin
	    a[i,j]:=-1;
		if i<>n then a[i+1,j]:=-1;
		if j<>n then a[i,j+1]:=-1;
		if i<>1 then a[i-1,j]:=-1;
		if j<>1 then a[i,j-1]:=-1;
	  end;
	end;
  readln;
  end;
end;

begin
  assign(input,'syndicate.in');
  reset(input);
  assign(output,'syndicate.out');
  rewrite(output);
  init;
  q[1].x:=1; q[1].y:=1; q[1].z:=0;q[1].used:=0;
  if n=1 then
  begin
    writeln(0);
	close(input);
	close(output);
	halt;
  end;
  l:=0;
  r:=1;
  hash[1,1,0]:=true;
  while true do
  begin
    l:=(l+1) mod max;
	for i:=1 to 4 do
	begin
	  xx:=q[l].x+d[i,1];
	  yy:=q[l].y+d[i,2];
	  if (xx=n)and(yy=n)then
	  begin
	    writeln(q[l].z+1);
		close(input);
		close(output);
		halt;
	  end;
	  if(xx>0)and(yy>0)and(xx<=n)and(yy<=n)then
	  begin
	    if a[xx,yy]=-1 then continue;
		now:=q[l].used;
		if(a[xx,yy]>=1)and(a[xx,yy]<=m)and(get[a[xx,yy]] and now=0) then//走到了供电装置,而该装置未关闭
		  inc(now,get[a[xx,yy]]);//关了它
		if hash[xx,yy,now] then continue;//已产生这种状态
	    if(a[xx,yy]>m)and(get[a[xx,yy]-m] and now=0) then continue;//走到了监视器,但是该监视器未被关闭
		r:=(r+1) mod max;//入队
		q[r].x:=xx;
		q[r].y:=yy;
		q[r].used:=now;
		q[r].z:=q[l].z+1;
		hash[xx,yy,now]:=true;//标记搜过
	  end;
	end;
  end;
end.
		
	


posted @ 2010-11-10 16:43  liukee  阅读(552)  评论(0编辑  收藏  举报