• 硬币问题 tarjan缩点+DP 莫涛


    2013-09-15 20:04

    题目描述

    有这样一个游戏,桌面上摆了N枚硬币,分别标号1-N,每枚硬币有一个分数C[i]与一个后继硬币T[i]。作为游戏参与者的你,可以购买一个名为mlj的小机器人,从任一个硬币处开始游戏,然后跳往该硬币的后继硬币T[i],直到你要它停下来,经过每个硬币时,你可以选择是否捡起它。当某个mlj机器人停下来后将被扔掉,这时你可以选择结束游戏或再买一个mlj机器人继续游戏。

    注意,每个硬币只能捡一次,而且你不能要求mlj跳向一个已被捡起的硬币或从一个已被捡起的硬币处开始游戏,因为那样会把mlj摔坏的。

     

    Your Task

    一开始你的得分是0,每购买一个mlj机器人将扣掉你M分,捡起一个硬币将得到对应的分数C[i],请问如何使得分尽量高(游戏过程中分数可以为负)。

     

    输入文件

    第一行两个正整数 N M

    接下来N行,每行两个正整数C[i] T[i]。

    输出文件

         一个整数,最大得分。

    样例输入

     4 2

     1 3

     2 3

     1 4

     1 3

    样例输出

     2

    数据约定

    30%   N<=10

    60%   N<=300

    100   N<=100000  1<=T[i]<=N

    运算过程及结果均在Longint范围内

    因为有N个点,N条边,且每个点都只有一个后继,所以可推知图中一定存在环,所以先用tarjan缩点,得到一颗上宽下窄的树(因为一个点只能有一个后继,而每个点可以成为好多点的后继),为了DP方便,缩点重新建图时,将边反向,这时得到了一颗多叉树,考虑到可能出现森林,所以用一个总根节点将每颗多叉树的根节点连接起来。

    然后我们得到了一颗多叉树,问题转化成了树形DP,由题意可知,因为到一个硬币可以不捡,所以机器人的路径可以重合,那么设W(X)代表从X节点向下走可以取得的最大值,假设X有多个儿子,因为当前有一个机器人由上方走来到X节点,所以X节点的儿子中最大的不用X重新买机器人,剩下的儿子中,如果W(P)>M,就相当于在P儿子处再买一个机器人,那么更新W(X)值,W(X):=W(P)-M;

    {$m 500000000}
    //By BLADEVIL
    var
        n, m                        :longint;
        father                      :array[0..200010] of longint;
        start                       :longint;
        flag, fseq                  :array[0..100010] of boolean;
        stack                       :array[0..100010] of longint;
        tot                         :longint;
        time                        :longint;
        low, dfn                    :array[0..100010] of longint;
        key                         :array[0..100010] of longint;
        color                       :longint;
        pre, last, other            :array[0..200010] of longint;
        l                           :longint;
        mark                        :array[0..200010] of longint;
        ans                         :longint;
    function min(a,b:longint):longint;
    begin
        if a>b then min:=b else min:=a;
    end;
    
    procedure connect(x,y:longint);
    begin
        inc(l);
        pre[l]:=last[x];
        last[x]:=l;
        other[l]:=y;
    end;
    
    procedure dfs(x:longint);
    var
        cur                         :longint;
    begin
        inc(tot);
        stack[tot]:=x;
        flag[x]:=true;
        fseq[x]:=true;
        inc(time);
        dfn[x]:=time;
        low[x]:=time;
        cur:=other[last[x]];
        if not flag[cur] then
            begin
                dfs(cur);
                low[x]:=min(low[x],low[cur]);
            end else
            if fseq[cur] then low[x]:=min(low[x],dfn[cur]);
    
        cur:=-1;
        if dfn[x]=low[x] then
        begin
            inc(color);
            while cur<>x do
            begin
                cur:=stack[tot];
                dec(tot);
                fseq[cur]:=false;
                key[cur]:=color;
                mark[color]:=mark[color]+mark[cur];
            end;
        end;
    end;
    
    procedure init;
    var
        i                           :longint;
        x                           :longint;
        p                           :longint;
    begin
        read(n,m);  tot:=0;  color:=n;
        for i:=1 to n do father[i]:=i;
        for i:=1 to n do
        begin
            read(mark[i],x);
            connect(i,x);
            father[x]:=i;
        end;
        for i:=1 to n do if father[i]=i then start:=i;
        if start=0 then inc(start);
        dfs(start);
        for i:=1 to n do if key[i]=0 then dfs(i);
    
        for i:=1 to n do
        begin
            p:=other[last[i]];
            if key[i]<>key[p] then
            begin
                connect(key[p],key[i]);
                father[key[i]]:=key[p];
            end;
        end;
        for i:=n+1 to color do if father[i]=0 then connect(color+1,i);
    
    end;
    
    function w(x:longint):longint;
    var
        p, q                        :longint;
        i, j, maxx                  :longint;
        sum                         :longint;
    begin
        q:=last[x];
        j:=0;
        w:=0;
        w:=w+mark[x];
        maxx:=0;
        while q<>0 do
        begin
            p:=other[q];
            sum:=w(p);
            if sum>m then w:=w+sum-m;
            if sum>maxx then maxx:=sum;
            q:=pre[q];
        end;
        if maxx<m then w:=w+maxx else w:=w+m;
    end;
    
    
    begin
        assign(input,'coin.in'); reset(input);
        assign(output,'coin.out'); rewrite(output);
        init;
        ans:=w(color+1)-m;
        if ans>0 then writeln(ans) else writeln(0);
        close(input); close(output);
    
    end.
  • 相关阅读:
    发布国内首个无服务器容器服务,运维效率从未如此高效
    阿里云异构计算团队亮相英伟达2018 GTC大会
    阿里如何将“高峰前扩容、高峰后缩容”的梦想照进现实?
    迁移到 GRUB 2
    (11计科1班-孙鹏启)SHELL脚本—期末成绩统计
    硬盘分区的c盘在外圈还是内圈
    Linux内核设计的艺术(第2版)
    卸载,弹出,安全移除驱动器 的区别
    (11级计科2班-张文旭)关于LINUX连接网络问题
    安装Zend Guard Loader
  • 原文地址:https://www.cnblogs.com/BLADEVIL/p/3433498.html
Copyright © 2020-2023  润新知