原创OI题目:部落冲突

部落冲突(coc)

Input:coc.in output:coc.out

题目描述:

    从前,有一个美丽的村庄,这里环境优美、社会安定、民风淳朴。在这样完美的村庄里,生活着一群完美的野蛮人ljj,他们在这里安家立业,并通过辛勤劳动制造出无限的money。然而,一个不幸的事发生了,一群名为hhh哥布林的恐怖地精部队为了那罪恶的money,竟无比邪恶的袭击了野蛮人ljj的村庄。他们抢走了money,毁坏了建筑,还无情的屠杀了大部分的野蛮人ljj,他们无恶不作、无所不为、简直就不是人。(呸,他们本来就不是人吗!)代表正义的野蛮人们现在无比的angry,为了能一雪前耻,抢回丢失的money,为那些在抵御哥布林hhh的战役中英勇牺牲的先烈们报仇,他们决定进攻hhh哥布林阵地。

    现在我们有哥布林阵地的一个r*c的一个地图,在这个地图中,有五种元素:平地、出入口、城墙、加农炮、金库。战争开始时有p个野蛮人,所有野蛮人站在入口处,战争过程的每一秒先后分为野蛮人行动加农炮攻击两个阶段。

    野蛮人行动阶段,野蛮人可以选择移动或抢钱(当野蛮人与某个未被抢光的金库处在同一坐标上时才可选择抢钱),以下为对移动和攻击的说明:

1、若野蛮人选择移动,则他们可以同时向上下左右四个方向移动一步,但他无法走到城墙和加农炮的格子上。

2、每个金库都有一个生命值,第i个金库生命值为hi。若野蛮人选择抢钱,则每个存活的野蛮人都会同时对与他们在同一坐标上的未被抢光的金库造成dd点伤害(dd输入会给出)。假如与野蛮人在同一坐标上的金库原来的剩余血量为hp,则该金库血量会变为(hp-dd*num)num为当前存活的野蛮人数。若金库血量变得小于或等于0,则此金库资源被抢光。(注意:若野蛮人前一秒选择抢钱且当前秒与他们在同一坐标的金库未被抢光,则他们当前秒必须选择抢钱!也就是当野蛮人选择抢某个金库时,他们必须一直抢到金库被抢完为止,不能中途离开。)

    加农炮攻击阶段,每一个加农炮会杀死在其攻击范围内一定数量的野蛮人(设第i个加农炮坐标为(xi,yi),野蛮人此时坐标为(xx,yy),则若|xi-xx|<=ti|yi-yy|<=titi为第i个加农炮的攻击范围),则这群野蛮人在此加农炮攻击范围内)。不同加农炮可杀死的野蛮人的数量也不同,对于第i个加农炮,它可杀死的野蛮人数量为di

      现在野蛮人的目标是抢光所有的金库后回到出入口。若中途某一时间结束后野蛮人全部阵亡,即野蛮人数量小于1,则战争失败,否则若有野蛮人能够安全回到出入口,则战争胜利。

     然而现在问题来了---ljj毕竟是野蛮人,他们的脑袋并未成熟,不知道如何进攻,而作为现代无比聪明的文明人,你能做他们的军师吗?作为一名优秀的军师,你要指挥野蛮人们在每一秒做出最优的决策,如果他们能胜利,就让存活的野蛮人数最大化;如果实在无法胜利,就让野蛮人全部英勇牺牲之前能偷的金库尽量多

输入解释:

    第一行6个整数p,r,c,n,m,dd,其中p表示野蛮人的初始数量,rc表示地图有rc列,nm分别表示加农炮的数量、金库的数量,dd表示每个野蛮人每秒对金库的伤害值。

    接下来r行每行包含c个字符,描述哥布林阵地的地图;其中“O”、“#”、“X”、“M”、“S”分别表示平地、城墙、加农炮、金库和出入口。(保证出入口不会在任何加农炮的攻击范围内)

    接下来n行中第i行四个整数,Xi,Yi,ti,di表示坐标为(Xi,Yi)的加农炮的各项属性。(xi表示第xi行,yi表示第yi列,ti表示攻击范围,di表示每秒能杀死的野蛮人数)

    接下来m行中第i行三个整数xi,yi,hi表示坐标为(xi,yi)的金库初始血量为hi

输入样例(coc.in)

100 4 4 1 2 2

OOOM

OXM#

OOO#

S###

2 2 1 5

1 4 800

2 3 550

输出样例(coc.out)

     若野蛮人可以胜利,则第一行输出1,第二行输出野蛮人回到出入口时最多有多少野蛮人存活;若野蛮人无法胜利,则第一行输出-1,第二行输出野蛮人在全部阵亡前最多可以抢光多少个金库。

输出样例:

1

30

数据规模:

对于50%:m<=8

另外20%数据保证野蛮人一定能胜利

对于100%:dd,ti<=10di<=10^9 p,hi<=10^18 r,c<=500 n<=10000 m<=15 1<=xi,Xi<=r 1<=yi,Yi<=c
样例解释:

OOOM

OXM#

OOO#

S###

    首先野蛮人移动到(2,3),此时还剩80个野蛮人。接着抢金库,抢完后还剩60个野蛮人。接着他们移动到另一个金库,此时剩余55个野蛮人。抢那个金库,由于无法受到加农炮的攻击,所以野蛮人还是55个。最后回到出入口,只剩30个野蛮人。

若先抢(1,4)金库,则最后只剩25个野蛮人。

附代码:

const yu=1000000;
var
    i,j,k,s,n,m,r,c,xx,yy,t,di,hx,hy,tot,ans:longint;
    p,dd:int64;
    x,y:array[0..15] of longint;
    h:array[1..15] of int64;
    map:array[1..500,1..500] of char;
    mapp:array[1..500,1..500] of longint;
    d:array[1..500,1..500] of int64;
    dis:array[0..15,0..15] of int64;
    way:array[1..4,1..2] of longint=((1,0),(-1,0),(0,1),(0,-1));
    data:array[1..yu,1..2] of longint;
    dt:array[1 ..500,1..500] of int64;
    bz:array[1..500,1..500] of boolean;
    f:array[0..15,1..65536] of int64;
    mi:array[0..15] of longint;
procedure bfs(xp:longint);
var
    i,j,k,ni,nj,pi,pj:longint;
begin
    fillchar(dt,sizeof(dt),10);
    i:=0; j:=1; dt[x[xp],y[xp]]:=0; bz[x[xp],y[xp]]:=true;
    data[1,1]:=x[xp]; data[1,2]:=y[xp];
    while i<>j do
    begin
        inc(i); if i>yu then i:=i-yu;
        ni:=data[i,1]; nj:=data[i,2];
        for k:=1 to 4 do
        begin
            pi:=ni+way[k,1]; pj:=nj+way[k,2];
            if (pi<=0) or (pi>r) or (pj<=0) or (pj>c) then continue;
            if (map[pi,pj]='#') or (map[pi,pj]='X') then continue;
            if dt[ni,nj]+d[pi,pj]<dt[pi,pj] then
            begin
                dt[pi,pj]:=dt[ni,nj]+d[pi,pj];
                if map[pi,pj]='S' then dis[xp,0]:=dt[pi,pj] else
                if map[pi,pj]='M' then dis[xp,mapp[pi,pj]]:=dt[pi,pj];
                if bz[pi,pj]=false then
                begin
                    bz[pi,pj]:=true;
                    inc(j); if j>yu then j:=j-yu;
                    data[j,1]:=pi; data[j,2]:=pj;
                end;
            end;
        end;
        bz[ni,nj]:=false;
    end;
end;
function steal(xi,yi:longint;num,hi:int64):int64;
var
    st,gc,sd,en:int64;
    l,r,mid:int64;
begin
    st:=num*dd; gc:=dd*d[xi,yi];
    if gc=0 then exit(num);
    l:=1; r:=st div gc;
    while l<r do
    begin
        mid:=(l+r) shr 1; en:=st-(mid-1)*gc;
        sd:=(st+en)*mid div 2;
        if sd>=hi then r:=mid else l:=mid+1;
    end;
    en:=st-(l-1)*gc;
    sd:=(st+en)*l div 2;
    if sd>=hi then exit(num-(l-1)*d[xi,yi]) else exit(0);
end;
procedure max(var a:int64;b:int64);
begin
    if b>a then a:=b;
end;
procedure dp;
var
    num:int64;
begin
    f[0,1]:=p;
    for s:=1 to mi[m+1]-1 do
    begin
            for i:=0 to m do if mi[i] and s>0 then
                if s=mi[m+1]-1 then f[0,mi[m+1]]:=f[i,s]-dis[i,0]
                else if f[i,s]>0 then
                    for j:=1 to m do if mi[j] and s=0 then
                    begin
                        num:=f[i,s]-dis[i,j];
                        max(f[j,s+mi[j]],steal(x[j],y[j],num,h[j]));
                    end;
    end;
end;
begin
    assign(input,'coc.in'); reset(input);
    assign(output,'coc.out'); rewrite(output);
    readln(p,r,c,n,m,dd);
    mi[0]:=1; for i:=1 to m+1 do mi[i]:=mi[i-1]*2;
    for i:=1 to r do
    begin
        for j:=1 to c do
        begin
            read(map[i,j]);
            if map[i,j]='S' then begin hx:=i; hy:=j; end;
        end;
        readln;
    end;
    for i:=1 to n do
    begin
        read(xx,yy,t,di);
        for j:=xx-t to xx+t do if (j>0) and (j<=r) then
            for k:=yy-t to yy+t do if (k>0) and (k<=c) then
                d[j,k]:=d[j,k]+di;
    end;
    for i:=1 to m do begin read(x[i],y[i],h[i]); mapp[x[i],y[i]]:=i; end;
    x[0]:=hx; y[0]:=hy;
    for i:=0 to m do bfs(i);
    dp;
    if f[0,mi[m+1]]>0 then begin writeln(1); writeln(f[0,mi[m+1]]); end
    else
    begin
        writeln(0);
        for s:=1 to mi[m+1] do
            for i:=1 to m do if f[i,s]>0 then
            begin
                tot:=0;
                for j:=1 to m do
                    if mi[j] and s>0 then inc(tot);
                if tot>ans then ans:=tot;
            end;
        writeln(ans);
    end;
    close(input); close(output);
end.

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值