[洛谷1528]切蛋糕

本题地址: http://www.luogu.org/problem/show?pid=1528

【题目描述】

 Facer今天买了n块蛋糕,不料被信息组中球球等好吃懒做的家伙发现了,没办法,只好浪费一点来填他们的嘴巴。他答应给每个人留一口,然后量了量每个人口的大小。Facer有把刀,可以切蛋糕,但他不能把两块蛋糕拼起来,但是他又不会给任何人两块蛋糕。现在问你,facer怎样切蛋糕,才能满足最多的人。(facer的刀很强,切的时候不会浪费蛋糕)。

【输入输出格式】

输入格式:

第一行n,facer有n个蛋糕。接下来n行,每行表示一个蛋糕的大小。再一行一个数m,为信息组的人数,然后m行,每行一个数,为一个人嘴的大小。(1<=n<=50, 1<=m<=1024)

输出格式:

一行,facer最多可以填多少张嘴巴。

【输入输出样例】

输入样例#1:

4
30
40
50
25
10
15
16
17
18
19
20
21
25
24
30

 

输出样例#1:

7

【思路】

鉴于打了太多注释,这里这简单说一下做法,如果单纯DFS的话样例或需要用5s,一部分优化可能过50%到70%,又容易推得贪心,DP一定不是正确的做法(反例好找的SO EASY)。AC的做法可以是二分人数,不断地DFS,然后把所有能想到的剪枝,什么可行性,最优性简直都放上,卡卡就能过了。因为代码复杂度太高,时间复杂度也不好算了,反正东卡西卡过了。

type sz=array[0..2000] of longint;

var a,b,sum:sz;
    n,i,x,y,st,lst,mid,m,summ,ans:longint;
    flag:boolean;

procedure qsort(r,l:longint;var a:sz);
var
     i,j,k:longint;
begin
     i:=r;
     j:=l;
     k:=a[(i+j) shr 1];
     repeat
          while a[i]<k do inc(i);
          while a[j]>k do dec(j);
          if i<=j then begin
              a[0]:=a[i];
              a[i]:=a[j];
              a[j]:=a[0];
              inc(i);
              dec(j);
          end;
     until i>j;
     if i<l then qsort(i,l,a);
     if r<j then qsort(r,j,a);
end;
//通用快排,由小到大的顺序

procedure dfs(beg,k:longint);
//b数组是排好序的,beg表示当前是从第几个蛋糕开始的
var
     i:longint;
begin
     if k=0 then 
        begin
            flag:=true;
            exit;
        end;
     if summ+sum[mid]>ans then exit;
     //优化,如果当前合法的蛋糕没有的值加上此刻处理人的蛋糕权值要比总权值要大,则退出。
     for i:=beg to n do 
        if a[i]>=b[k] then 
            begin
                a[i]:=a[i]-b[k];
                if a[i]<b[1] then inc(summ,a[i]);
                //summ表示没有用的蛋糕数量,如果第一个人都无法满足那么蛋糕a[i]等于没用
                if b[k]=b[k-1] then dfs(i,k-1) else dfs(st,k-1);
                //如果b[k]=b[k+1],那么只有i到n这些蛋糕可取(想想这是为什么)
                if a[i]<b[1] then dec(summ,a[i]);
                a[i]:=a[i]+b[k];
                //两行回溯
                if flag then exit;
            end;
end;

begin
     readln(n);
     for i:=1 to n do readln(a[i]);
     readln(m);
     for i:=1 to m do readln(b[i]);
     qsort(1,n,a);
     qsort(1,m,b);
     st:=1;
     while (a[st]<b[1]) and (st<n) do inc(st);    
     //把没用的蛋糕去掉,处理后st是能用的蛋糕
     for i:=st to n do inc(ans,a[i]);
     lst:=m;
     while (b[lst]>a[n]) and (lst>1) do dec(lst); 
     //把不能满足的人去掉,现在只需要满足lst个人就行了
     for lst:=1 to lst+1 do 
        begin
            sum[lst]:=sum[lst-1]+b[lst];
            //sum[i]表示到i人吃的蛋糕总数
            if sum[lst]>ans then break;
       
//所有蛋糕加起来都无法满足可以的人那么退出
end; dec(lst);//从上层循环的影像中退出来 x:=0; y:=lst; while x<y do //需要满足的人数小于所有的人数 begin mid:=(x+y+1) shr 1; //二分查找的是人,位运算:shr 1==>div 2; flag:=false; summ:=0; dfs(st,mid); if flag then x:=mid else y:=mid-1; end; writeln(x); end.

 

posted on 2015-08-12 17:34  川汉唐  阅读(384)  评论(0编辑  收藏  举报

导航