CF101E Candies and Stones题解

[题目描述]

Little Gerald and his coach Mike play an interesting game. At the beginning of the game there is a pile consisting of n candies and a pile consisting of m stones. Gerald and Mike move in turns, Mike goes first. During his move Mike checks how many candies and stones Gerald has eaten. Let Gerald eat a candies and b stones. Then Mike awards Gerald f(a, b) prize points. Gerald during his move either eats a candy from the pile of candies or a stone from the pile of stones. As Mike sees that Gerald has eaten everything apart one candy and one stone, he awards points for the last time and the game ends. Gerald is not allowed to eat all the candies, and he is not allowed to eat all the stones too. Tell Gerald how to play to get the largest possible number of points: it is required to find one of the possible optimal playing strategies for Gerald.

[翻译]

         小A和小B玩游戏,有n堆石子和m堆糖果,小B先动。

         小A的操作,取走一堆石子或一堆糖果。

         小B的操作,若截止到此轮取走了i堆石子,j堆糖果,则分值加上(xi+yj)%p

         要求:直至全部拿完,分值最大,并输出方案(蒯自天聪)

[时限]15s

[空限]45MB

[题解]

        N^2的dp应该都知道.问题是如何在卡空间的情况下输出方案.

        使用滚动数组是势在必行的,但如何在使用滚动数组的情况下输出方案呢?

        一个最裸的想法是:第一次DP到Fn+m-1,第二次重新DP到Fn+m-2,输出方案,然后一直做N次DP,就可以输出所有方案了.

        显然,这样做铁定TLE.但是,这给我们提供了一个很好的思路.

        为了达到时间与空间的平衡,我们将DP的方程按sqrt(N)分块,每隔sqrt(n)行把所有状态记住.然后从后往前DP输出方案.

        输出方案时进行sqrt(n)次DP,每次DP时用一个sqrt(n)*m的数组记住状态,就可以把这一段的方案搞出来了.

        因为每个状态只会被算到2次,所以总的时间复杂度是O(n*n),空间复杂度是O(sqrt(n)*m),因为我写的状态是Fi,j表示选了i个有j个糖果,所以n=40000,跑起来奇慢无比(加上各种开关10s,不加开关14s).
        因为第一次打分块,一开始块太大,拍的数据太小,结果一直搞不出错在哪里,交上去全WA.

        tourist有一种很NB的方法:用Fi,j表示取了i个石头,j个糖果.这样空间就比我少了很多.于是,他就可以多加一个数组,记录每sqrt(m)列的状态!!!!

        于是,tourist的状态就被划分成若干sqrt(n)*sqrt(m)的子矩阵,从Fn,m递归下去,对每个子矩阵进行dp求出方案,只要遍历O(n)个矩阵,总的复杂度为O(N*N+sqrt(n)*m),几乎只有裸DP的复杂度!!!!!虽然理论的复杂度和我的一样,但是常数完爆我的方法,时间也是我的1/3.

我的Code:

program NoName;
const skip=200;
type
        int=longint;
        arr=array[0..20000]of int;
        point=^arr;
var
        i,j:longint;
        tot,k,m,n,max,min,now:int;s,p:int;
        f,g,wx,wy:arr;
        prev,next,temp:point;
        list:array[0..201]of arr;
        ff:array[0..skip+1]of arr;
        pos:array[1..201]of int;
        solution:array[0..40000]of char;

procedure getans(x:int);var i,j:longint;
begin
        fillchar(ff,sizeof(ff),0);
        ff[0]:=list[x];s:=pos[x+1]-pos[x];
        for i:=1 to s do begin
                min:=0;max:=m;p:=i+pos[x];
                if p<m then max:=p;
                if(min<p-n)then min:=p-n;
                for j:=min to max do begin
                        if(j<>0)and(ff[i-1,j-1]>ff[i-1,j])then ff[i,j]:=ff[i-1,j-1]
                                else ff[i,j]:=ff[i-1,j];
                        ff[i,j]:=ff[i,j]+(wx[p-j]+wy[j])mod k;
                end;
        end;
        for i:=pos[x+1]-1 downto pos[x] do begin
                s:=i-pos[x];
                if(now=0)or(ff[s,now]>ff[s,now-1])then begin
                        solution[i]:='C';
                end else begin
                        solution[i]:='S';dec(now);
                end;
        end;
end;

procedure dp;
begin
        f[0]:=(wx[0]+wy[0])mod k;
        prev:=@f;next:=@g;
        tot:=1;list[1]:=f;
        for i:=1 to n+m do begin
                min:=0;max:=m;
                if i<m then max:=i;
                if(min<i-n)then min:=i-n;
                for j:=min to max do begin
                        if(j<>0)and(prev^[j]<prev^[j-1])then next^[j]:=prev^[j-1]
                                else next^[j]:=prev^[j];
                        next^[j]:=next^[j]+(wx[i-j]+wy[j])mod k;
                end;
                if(i mod skip=0)and(i<>n+m)then begin
                        inc(tot);
                        for j:=min to max do list[tot][j]:=next^[j];
                        pos[tot]:=i;
                end;
                temp:=prev;prev:=next;next:=temp;
        end;
        writeln(prev^[m]);
        inc(tot);list[tot]:=prev^;pos[tot]:=n+m;
        now:=m;
        for i:=tot-1 downto 1 do getans(i);
        for i:=0 to n+m-1 do write(solution[i]);
end;

begin
        assign(input,'E.in');reset(input);
        assign(output,'E.out');rewrite(output);
        read(n,m,k);
        dec(n);dec(m);
        for i:=0 to n do read(wx[i]);
        for i:=0 to m do read(wy[i]);
        dp;
        close(input);close(output);
end.

tourist的程序自己去CF上看吧.


BY QW

转载请注明出处

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值