[USACO 1.3.2] Barn Repair

[题目描述]

Barn Repair

修理牛棚

        在一个暴风雨的夜晚,农民约翰的牛棚的屋顶、门被吹飞了。 好在许多牛正在度假,所以牛棚没有住满。 剩下的牛一个紧挨着另一个被排成一行来过夜。 有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。 自门遗失以后,农民约翰很快在牛棚之前竖立起新的木板。 他的新木材供应者将会供应他任何他想要的长度,但是供应者只能提供有限数目的木板。 农民约翰想将他购买的木板总长度减到最少。 给出 M(1<= M<=50),可能买到的木板最大的数目;S(1<= S<=200),牛棚的总数;C(1 <= C <=S) 牛棚里牛的数目,和牛所在的牛棚的编号stall_number(1 <= stall_number <= S),计算拦住所有有牛的牛棚所需木板的最小总长度。 输出所需木板的最小总长度作为的答案。 

PROGRAM NAME: barn1 

INPUT FORMAT

第 1 行: M , S 和 C(用空格分开)
第 2 到 C+1行: 每行包含一个整数,表示牛所占的牛棚的编号。

SAMPLE INPUT (file barn1.in) 

4 50 18
3
4
6
8
14
15
16
17
21
25
26
27
30
31
40
41
42
43

OUTPUT FORMAT

单独的一行包含一个整数表示所需木板的最小总长度。 

SAMPLE OUTPUT (file barn1.out) 

25 

[ 一种最优的安排是用板拦住牛棚3-8,14-21,25-31,40-43.]


[解题思路]

可以这样想,先将一块长度为S的木板覆盖所有牛棚,可以每次将木块分裂,就相当于多用一块木板了,要使所有木块长度总和最小,贪心的想下,必定是每次分裂的时候取间隔最大的。于是算法就出来了,将所有间隔按长度降序排序,除去前面M-1个即可。特别注意的是,两端的间隔是不计入排序的,因为完全不必用木板去盖。

由于数据比较水,所以还想了一个复杂度较高的DP。

记F[i,j]表示修到第i个位置,用j块木板的最小长度。

则,当i不需要覆盖时,F[i,j]=min(F[i,j],F[i-1,j]);当i需要覆盖时,F[i,j]=min(F[i,j],F[k,j-1]+i-k)。

总复杂度是O(S^2*M),依旧可以0.00s过...


[Code]

贪心:

{
ID: zane2951
PROG: barn1
LANG: PASCAL
}

program barn1;
var
   w:array[0..211] of longint;
   f:array[0..211] of boolean;
   n,m,s,x,i,t,len,ans,st,ed:longint;

//-----------qs------------
procedure qs(ss,tt:longint);
var
   i,j,ce,tmp:longint;

begin
   i:=ss; j:=tt; ce:=w[(i+j)>>1];
   repeat
      while w[i]>ce do inc(i);
      while w[j]<ce do dec(j);
      if i<=j then
         begin
            tmp:=w[i]; w[i]:=w[j]; w[j]:=tmp;
            inc(i); dec(j);
         end;
   until i>j;
   if i<tt then qs(i,tt); if ss<j then qs(ss,j);
end;

//-----------min-----------
function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

//----------main-----------
begin
   assign(input,'barn1.in'); reset(input);
   assign(output,'barn1.out'); rewrite(output);
   readln(n,m,s);
   for i:=1 to s do begin readln(x); f[x]:=true; end;
   for i:=1 to m do if f[i] then begin st:=i; break; end;
   for i:=m downto 1 do if f[i] then begin ed:=i; break; end;
   t:=0; len:=0; f[m+1]:=true;
   for i:=st to ed do
      if f[i] then if len>0 then begin inc(t); w[t]:=len; len:=0; end
         else else inc(len);
   qs(1,t); ans:=ed-st+1;
   for i:=1 to min(n-1,t) do ans:=ans-w[i];
   if ans>=0 then writeln(ans) else writeln(0);
   close(input); close(output);
end.
DP:

{
ID: zane2951
PROG: barn1
LANG: PASCAL
}

program barn1;
var
   f:array[0..211,0..51] of longint;
   g:array[0..211] of boolean;
   n,m,s,i,x,ans,j,k,tmp,ce,st,ed:longint;

//-----------min-----------
function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

//----------main-----------
begin
   assign(input,'barn1.in'); reset(input);
   assign(output,'barn1.out'); rewrite(output);
   readln(n,m,s);
   for i:=1 to s do begin readln(x); g[x]:=true; end;
   for i:=1 to m do if g[i] then begin st:=i; break; end;
   for i:=m downto 1 do if g[i] then begin ed:=i; break; end;
   fillchar(f,sizeof(f),127);
   for i:=0 to n do f[st-1,i]:=0;
   for j:=1 to n do
      for i:=st to ed do
         if not g[i] then f[i,j]:=min(f[i,j],f[i-1,j])
            else
               begin
                  ce:=maxlongint;
                  for k:=st-1 to i-1 do
                     if f[k,j-1]+i-k<ce then ce:=f[k,j-1]+i-k;
                  f[i,j]:=min(f[i,j],ce);
               end;
   ans:=maxlongint;
   for i:=1 to n do ans:=min(ans,f[ed,i]);
   writeln(ans);
   close(input); close(output);
end.
发布了41 篇原创文章 · 获赞 0 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章