• [NOIP2007]矩阵取数游戏


    NOIP 2007 提高第三题

    题目描述

    帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数。游戏规则如下:
    1.每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;
    2.每次取走的各个元素只能是该元素所在行的行首或行尾;
    3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);
    4.游戏结束总得分为m次取数得分之和。
    帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

    输入输出格式

    输入格式:

    输入文件game.in包括n+1行:
        第1行为两个用空格隔开的整数n和m。
        第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开的非负整数。

    数据范围:
    60%的数据满足:1<=n, m<=30,答案不超过10^16
    100%的数据满足:1<=n, m<=80,0<=aij<=1000

    输出格式:

    输出文件game.out仅包含1行,为一个整数,即输入矩阵取数后的最大得分。

    输入输出样例

    输入样例#1:

    2 3
    1 2 3
    3 4 2
    

    输出样例#1:

    82

    思路

    TIPS高精度计算,压位,DP,分治
    DP:f[i,j]表示这一行左边取到第i位,右边取到第j位时的最大得分,转移方程:f[i,j]:=max(f[i-1,j]+a[i-1]*(2的n次方),f[i,j+1]+a[j+1]*(2的n次方));
    怎么理解转移方程:

    举例说明,有矩阵:
    a1,a2,a3……an

    b1,b2,b3……bn 有N列,但只有2行(这里考虑到书写,所以只列举了2行,大于2行也可以证明)

    假设上面矩阵的最大得分取法是:a1*2+b1*2 + a2*22+b2*22……+an*2n+bn*2n,把这个算式变换一下,将an和bn的分开提取,结果变换为a1*2+a2*22……+an*2n + b1*2+b2*22……+bn*2n。我们可以考察一下这个算式会发现:a1*2+a2*22……+an*2n 其实就是第一行a1,a2,a3……an的最大得分, b1*2+b2*22……+bn*2n也是第二行b1,b2,b3……bn 的最大得分。也就是说矩阵的最大得分其实是每一行的最大得分之和,每一行的取数不会和其他行发生联系或冲突的。于是矩阵取数最大得分问题就分解为行取数最大得分问题,只要求出每一行的最大得分,然后求和,便可得出矩阵的最大得分。

    再来考察一下单行取数问题的求解。首先,设函数Maxgame(i,j)(i<j),这个函数的功能是:求解一行 ai,a(i+1),a(i+2)……aj 的最大得分。那么a1,a2,a3……an的最大得分用这个函数来表示就是:Maxgame(1,n),那么这个Maxgame(1,n)的值怎样求得呢?我们继续研究。由于取数的时候只能取行首数或是行尾数,于是便有:Maxgame(1,n)=Max((a1*2+Maxgame(2,n)*2),(an*2+Maxgame(1,n-1))),从1到n的行最大得分要么等于取行首元素*2+从2到n的行的最大得分*2;要么等于取行尾元素*2+从1到n-1的行的最大得分。所以,归纳起来Maxgame(1,n)就只有这2种可能性。再看看,此时的问题就被分解为了一个相同的问题,只是数据规模小了1:去掉了a1或an,剩下的a2,a3,……an或a1,a2,……a(n-1)成为新的待求最大得分的行。如此进行下取,当这一行只剩下2个数时:ax,ay(x必然等于y-1,ax和ay是相邻的2个数),则只需要先取min(ax,ay),最后一步取max(ax,ay)。该问题迎刃而解。

    解释来源:http://mynoi.blog.163.com/blog/static/8356718420085351216351/
    const ma=10000;//压到万进制
    
    type arr=array[0..80] of longint;//这是一个高精度数
    
    var f:array[-1..82,-1..82] of arr;
        a:array[0..81,0..81] of longint;
        s2:array[0..80] of arr;
        i,j,k,n,m:longint;
        ss,sum,a1,a2,a3,a4:arr;
    
    function max(a,b:arr):arr;
    var z:longint;
    begin
        if a[0]>b[0] then exit(a);
        if b[0]>a[0] then exit(b);
        for z:=a[0] downto 1 do
            begin
                if a[z]>b[z] then exit(a);
                if b[z]>a[z] then exit(b);
            end;
        exit(a);
    end;
    //高精度比较大小
    
    function plus(a,b:arr):arr;
    var z,l:longint;
    begin
        if a[0]>b[0] then l:=a[0] else l:=b[0];
        if l=0 then l:=1;
        for z:=1 to l do
            begin
                a[z+1]:=a[z+1]+(a[z]+b[z]) div ma;
                a[z]:=(a[z]+b[z]) mod ma;
            end;
        plus:=a;
        if a[l+1]<>0 then plus[0]:=l+1 else plus[0]:=l;//处理最高位
    end;
    //高精度加法
    
    function multiply(b:longint;a:arr):arr;
    var l,z:longint;
    begin
        l:=a[0];
        if l=0 then l:=1;
        for z:=1 to l do
            a[z]:=a[z]*b;
        for z:=1 to l do
            begin
                a[z+1]:=a[z+1]+a[z] div ma;
                a[z]:=a[z] mod ma;
            end;
        while a[l+1]<>0 do
            begin
                inc(l);
                a[l+1]:=a[l] div ma;
                a[l]:=a[l] mod ma;
            end;
        //处理最高位
        multiply:=a;
        multiply[0]:=l;
    end;
    //高精度乘法
    
    procedure init;
    var i,j:longint;
    begin
        readln(n,m);
        for i:=1 to n do
            begin
                for j:=1 to m do
                    read(a[i,j]);
                readln;
            end;
    end;
    
    procedure change;
    begin
        fillchar(a,sizeof(a),0);
        fillchar(f,sizeof(f),0);
        fillchar(sum,sizeof(sum),0);
    end;
    
    procedure work;
    var i,j:longint;
    begin
        s2[1][1]:=2;
        s2[1][0]:=1;
        for i:=2 to m do s2[i]:=multiply(2,s2[i-1]);
    end;
    
    procedure main;
    var i,j,k:longint;
    begin
        for k:=1 to n do
            begin
                for i:=0 to m do
                    for j:=m+1 downto i+1 do
                        begin
                            a1:=multiply(a[k,i],s2[i+m-j+1]);
                            a2:=multiply(a[k,j],s2[i+m-j+1]);
                            a3:=plus(f[i-1,j],a1);
                            a4:=plus(f[i,j+1],a2);
                            f[i,j]:=max(a3,a4);
                        end;
                ss:=f[0,1];
                for i:=1 to m-1 do
                    ss:=max(ss,f[i,i+1]);
                sum:=plus(sum,ss);
            end;
    end;
    
    procedure printf;
    var i:longint;
    begin
        write(sum[sum[0]]);
        for i:=sum[0]-1 downto 1 do
            begin
                if sum[i]<10 then write('000');
                if (sum[i]>=10)and(sum[i]<100) then write('00');
                if (sum[i]>=100)and(sum[i]<1000) then write('0');
                write(sum[i]);
            end;
        writeln;
    end;
    
    begin
        change;
        init;
        work;
        main;
        printf;
    end.
    View Code
    
    
    
     
     

     

  • 相关阅读:
    CTeX里面CTRL-Space和中文输入法的冲突问题解决
    用LaTeX画树形结构
    统计学howto
    Lights Out Game
    ubuntu下安装 Source insight
    github常用命令
    编程珠玑:第7章(初略估算)的阅读体会
    在windows上安装common lisp开发环境
    睡眠十律:程序员必看
    网络和服务器编程
  • 原文地址:https://www.cnblogs.com/yangqingli/p/4735607.html
Copyright © 2020-2023  润新知