usaco第二章我的所有题解和程序(三) usaco 2.3

http://hi.baidu.com/mfs666/blog/item/d33d4f90d038098ba977a41a.html

 

usaco 2.3.1 The Longest Prefix (prefix)

串的动规,用了比较简单的一种一维动规,就是f[i]是这一位是否可以排出,状态转移是枚举每个元素串,如果有任何一个的减去长度的位置可以排成,且s[i]的后缀等与此元素串则f[i]为true,这样的时间复杂度是类似O(mn),但是实际上数据很弱,所以不用优化也绝对不会TLE(其实给的数据最大的用了0.1S),但是其实有很多优化可以用,比如元素串最大长度为10,所以如果连续10个f都是false,就可以停止了,还可以用字符串的hash或者trie树优化枚举,那样就成了o(10n)了。

code

{
ID: mfs.dev2
PROG: prefix
LANG: PASCAL
}
program prefix;

var

m:array[0..201] of string;
l:array[0..201] of longint;
f:array[-1..200001] of boolean;
s,si:ansistring;
i,j,c,r,k:longint;


begin
assign(input,'prefix.in');
assign(output,'prefix.out');
reset(input);rewrite(output);
readln(si);
c:=1;
while si<>'.' do begin
   for i:=1 to length(si) do begin
    if si[i]=' ' then begin
     inc(c);
     continue;
    end;
    m[c]:=m[c]+si[i];
   end;
   readln(si);
   inc(c);
end;

while not eof do begin
readln(si);
s:=s+si;
end;
readln(si);
s:=s+si;
fillchar(f,sizeof(f),0);
f[0]:=true;
for i:=1 to c do
   l[i]:=length(m[i]);
k:=length(s);
for i:=1 to k do begin

   for j:=1 to c do begin
    if i-l[j]<0 then
     continue;
    if f[i-l[j]] then
     if copy(s,i-l[j]+1,l[j])=m[j] then begin
      f[i]:=true;
      break;
     end;
   end;
end;

for i:=1 to k do
   if f[i] then
    r:=i;
writeln(r);
close(output);
end.

usaco 2.3.2 Cow Pedigrees (nocows)

囧动态规划,树的计数问题,但是没想到方程,看题解,也不给个具体的说明或者证明,而且取模的性质我也不了解,先囧着吧,等找只神牛问。。。

code

{
ID: mfs.dev2
PROG: nocows
LANG: PASCAL
}
program nocows;

var
f:array[-2..201,0..101] of longint;
i,j,o,n,k,r:longint;

begin
assign(input,'nocows.in');
assign(output,'nocows.out');
reset(input);
rewrite(output);
readln(n,k);
for i:=1 to k do
   f[1,i]:=1;
f[1,1]:=1;

for i:=1 to n do
    for j:=1 to k do
    for o:=1 to i-2 do
     inc(f[i,j],f[o,j-1]*f[i-1-o,j-1] mod 9901);

   r:=f[n,k]-f[n,k-1];
   while r<0 do
    inc(r,9901);
   r:=r mod 9901;

   writeln(r);
close(output);
end.

usaco 2.3.3 Zero Sum (zerosum)

模拟和深搜,时间复杂度没问题,水题,一次AC。

code

{
ID: mfs.dev2
PROG: zerosum
LANG: PASCAL
}


program zerosum;

var
i,n,j,cn:longint;
s,t:string;
rs:array[0..10000] of string;

procedure ca;
var
   a,c:string;
   b,bb,x,y,i,h:longint;
begin
   h:=0;
   a:='';
   c:='';
   for i:=1 to length(s) do
    begin
     if s[i] in ['1'..'9'] then begin
      if h=1 then
       c:=c+s[i];
      if h=0 then
       a:=a+s[i];
     end
     else begin
      bb:=b;
      case s[i] of
       '+': b:=1;
       '-': b:=2;
      end;
      if h=0 then begin
       h:=1;
       continue;
      end;
      val(a,x);val(c,y);
      if bb=1 then
       x:=x+y
      else
       x:=x-y;
      str(x,a);c:='';
    end;
   end;
    x:=0;y:=0;
    val(a,x);val(c,y);
    if h=1 then
     if b=1 then
      x:=x+y
     else
      x:=x-y;
    if x=0 then begin
     inc(cn);
     rs[cn]:=s;
    end;
end;

procedure dfs(p:integer);
var
   t,tn,sc:string;
   i:integer;
begin
   if p>n-2 then begin
    ca;
    exit;
   end;
   sc:=s;
   for i:=1 to 3 do begin
    case i of
     1:t:='+';
     2:t:='-';
     3:t:='';
    end;
    str(p+2,tn);
    s:=s+t+tn;
    dfs(P+1);
    s:=sc;
   end;
end;

begin
assign(input,'zerosum.in');
assign(output,'zerosum.out');
reset(input);rewrite(output);

readln(n);

s:='1';
dfs(0);

for i:=1 to cn do begin
j:=2;
while j<=length(rs[i]) do begin
if (rs[i][j] in ['1'..'9']) and (rs[i][j-1] in ['1'..'9']) then
    insert(' ',rs[i],j);
inc(j);
end;
end;
for i:=1 to cn-1 do
for j:=i+1 to cn do
if rs[j]<rs[i] then begin
   t:=rs[j];
   rs[j]:=rs[i];
   rs[i]:=t;
end;
for i:=1 to cn do
   writeln(rs[i]);

close(output);
end.

usaco 2.3.4 Money Systems (money)

动规问题,无限背包的计数。自己想的方程是这样:

for i:=1 to v do
   for j:=1 to n do begin
    t:=j;
    o:=0;
    while t>=0 do begin
     inc(f[i,j],f[i-1,t]);
     inc(o);
     t:=j-m[i]*o;
    end;
   end;

但是太垃圾了,有一个点会TLE(就是那种数据会使里面的while循环执行很多次),于是看题解得到O(mn)的方程,也就是

for i:=1 to m do
    for j:=0 to n do
    begin
      dp[i,j]:=dp[i-1,j];
      if j-cc[i]>=0 then
        dp[i,j]:=dp[i,j]+dp[i,j-cc[i]];
    end;

根据hzhua神牛的解释,这个就是转化为01背包,dp[i,j]:=dp[i,j]+dp[i,j-cc[i]];中的dp[i,j-cc[i]]意思就是用过了i,还想用i(01的是f[i-1,j-cc[i]])这样每次都是再决定是否再用一次,而之前的用这个组成的更小的容量已经求出,转移即可。要注意f[0,0]也要初始化成1,而后面会求到的几个要注意不要犯傻也把它初始化了,那样就重复了。初始化要注意只能处理会用到但不会求的边界。

code:

{
ID: mfs.dev2
PROG: money
LANG: PASCAL
}
var
v,i,m:byte;
n,j:word;
cc:array[1..25]of word;
dp:array[0..25,0..10000]of int64;
begin
assign(input,'money.in');
assign(output,'money.out');
reset(input);
rewrite(output);
readln(v,n);
fillchar(dp,sizeof(dp),0);
m:=1;
for i:=1 to v do
begin
    read(cc[m]);
    for j:=1 to m do
      if cc[j]=cc[i] then
        break;
    if j=m then
      inc(m);
end;
dec(m);
readln;
for i:=1 to v do
   dp[i,0]:=1;
dp[0,0]:=1;
for i:=1 to m do
    for j:=0 to n do
    begin
      dp[i,j]:=dp[i-1,j];
      if j-cc[i]>=0 then
        dp[i,j]:=dp[i,j]+dp[i,j-cc[i]];
    end;
writeln(dp[m,n]);
close(output);
end.

usaco 2.3.5 Controlling Companies (concom)

有意思的题。这个题的我的算法类似佛洛伊德,时间近似O(n^3)但是其实实际上要低得多,而且n最大100,时间复杂度不存在任何问题。开邻接矩阵记录原始股(囧词。。。),同时开邻接表记录每个公司的控制名单,然后再开一个邻接矩阵,记录每个控制关系是否已经存在。每次扫描全部公司,按照给出的规则看看是否会有之前没有存在的控制关系出现,如果有则增加计数器,同时计入邻接矩阵和对应公司的邻接表,重复这个过程直到没有新的控制关系出现。这样只要有的控制都会被记录,如果没有新的控制,那么结果就不会再改变,这样就退出,真的很类似弗洛伊德算法的精神。。。

其实还可以写出类似松弛操作的算法(记录新增的控制关系,下一次只检查有关的公司),那样会更快,不过没有必要啦。

code:

{
ID: mfs.dev2
PROG: concom
LANG: PASCAL
}
program concom;

type v=record
n:integer;
d:array[0..101] of integer;
end;

var
rt,re:array[1..101,1..101] of integer;
rx:array[1..101] of v;
i,j,n,l1,l2,b,cc:integer;

function ga(x,y:integer):integer;
var
   i:integer;
begin
   ga:=0;
   for i:=1 to rx[x].n do
    inc(ga,rt[rx[x].d[i],y]);
end;

begin
assign(input,'concom.in');
assign(output,'concom.out');
reset(input);
rewrite(output);
readln(n);
for i:=1 to n do begin
   readln(l1,l2,b);
   rt[l1,l2]:=b;
   {if b>50 then begin
    inc(cc);inc(c);
    rc[cc]:=l1;
    rd[cc]:=l2;
    ra[cc]:=l1;
    rb[cc]:=l2;
    re[l1,l2]:=1;
   end;}
end;
for i:=1 to 101 do begin
   rt[i,i]:=100;
   re[i,i]:=1;
   rx[i].n:=1;
   rx[i].d[1]:=i;
end;
cc:=1;

while cc>0 do begin
   cc:=0;
   for i:=1 to 100 do
    for j:=1 to 100 do
     if (re[i,j]=0) and (ga(i,j)>50) then
      begin
       re[i,j]:=1;
       inc(cc);
       inc(rx[i].n);
       rx[i].d[rx[i].n]:=j;
      end;
   end;

for i:=1 to 100 do
   for j:=1 to 100 do
    if (re[i,j]=1) and (i<>j) then
     writeln(i,' ',j);
close(output);
end.

posted @ 2008-12-11 12:30  jesonpeng  阅读(263)  评论(0编辑  收藏  举报