[題目描述]
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.