[USACO 1.3.2] Barn Repair

本文探讨了农民约翰如何利用有限的木材资源,最有效地修复被暴风雨破坏的牛棚,通过贪心算法和动态规划两种方法解决实际问题。文中详细介绍了两种解题思路,并附有代码实现,旨在提供一种优化策略,减少木板总长度的需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[题目描述]

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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值