[最大费用最大流] [记忆化搜索] [Vijos P1653] 疯狂的方格取数 (getnum)

背景 Background

Due to the talent of talent123,当talent123做完NOIP考了两次的二取方格数和vijos中的三取方格数后,突发奇想…

题目描述 Description

在一个宽 M M M,长 N N N的矩阵中,请你编一个程序, n n n次从矩阵的左上角走到矩阵的右下角,每到一处,就取走该处的数字,请你选择一
种走法使取得的数字的和最大,并输出其最大值。其中:$3<=M<=20 ,M<=N<=100 ,1<=n<=10 $
如输入数据:
3 10 13
0 1 2 3 4 9 7 1 3 1
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 5
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 5
9 1 2 2 3 6 7 8 1 2
1 2 3 4 5 9 8 7 6 1
9 7 1 3 1 9 1 2 2 3
6 7 8 1 2 1 2 3 4 0
其中 n = 3 , M = 10 , N = 13 n=3,M=10,N=13 n=3,M=10,N=13
即当 n = 3 n=3 n=3时,就相当于是3取方格数。
对于以上的数据:
将输出: 297 297 297
//注:如过你想到了无记忆性搜所的方法(不管你怎样优化),你可以直接放弃这道题了。
//提示1:动态规划如果用的是二位数组,规模为 100 ∗ 100000 100*100000 100100000即可。
//提示2:如果你坚信自己的程序已经无可优化了,可有 2 2 2个数据依然超时,那么告诉你,存在数据有 M < n M<n M<n的情况!!!

输入 Input

第一行:三个整数: n , M , N n,M,N n,M,N
以下的 N N N行每行 M M M个数字,代表你要处理的矩阵。

输出 Output

只有一行:你所取得的数字的和。

样例输入 Sample Input

4 6 7
0 2 3 4 5 6
6 5 4 3 2 1
0 9 8 7 6 5
12 3 4 5 6 7
0 0 0 1 2 3
12 23 34 45 1 23
4 5 6 6 1 0

样例输出 Sample Output

265

限制 Limits

数据范围见题目
共有 10 10 10个测试数据,每个测试数据包含 1 1 1个测试点,每个测试点的时间限制为 2 2 2秒钟。
Time Limit 😒 2s$ & Memory Limit : 128 M B 128MB 128MB

来源 Source

本题目来自:北京市,中关村中学,高三9班,孙一(网名:talent123),联系方式:865383864(QQ)

这道题是PoPoQQQ在培训时讲的,直接上最大费用最大流,模板水过…
思路:拆点建边,一个格子拆成两个点,这两点之间有一条流量为 1 1 1,权值为 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j]的边,向四周格子连流量为INT_MAX,权值为 0 0 0的边。最后超级源 S S S ( 1 , 1 ) (1,1) (1,1)连流量为 n n n,权值为 0 0 0的边, ( N , M ) (N,M) (N,M)向超级汇连流量为 n n n,权值为 0 0 0的边。
连边注意不要溢出范围…(别连到外面去)
还有超级源和超级汇的选择
然后就是代码了…
Code

题解貌似是记忆化搜索?

#include <stdio.h>
#include <math.h>
int move[100][100000]={};//move[step][status];//status是一个 M进制, time位 的状态存储量;
int time,map[100][20],M,N;//横 M <20,竖 N<100,M<N,规模:M+N<=100,time<=10,((2M)^time)*(M+N)<=10000000;
int trial;//trial=2^time; //trial是一个 2进制,time位 的增量存储变量 ;
int fp(int step,int status)
{
    if(move[step][status]!=0)return move[step][status];//曾经已经求出过此状态的值 ;
    int temp=0,max;//temp:此状态下,覆盖的值!   max:从此开始(包括此状态)一直到终止状态的最大值,赋temp为初值!;
    int flag[100][20]={};//一个位置的值是否被取过了;
    int rem=status,i;
    int list[10];
    for(i=0;i<time;i++)//计算:temp;
    {
        int x=rem%M,y=step-x;   //翻译 ;
        list[i]=x;
        if(flag[y][x]==0)  //之前这个地方没有被取过
        {
            temp+=map[y][x];
            flag[y][x]=1;
        }
        rem/=M;
    } //for
    max=temp;
    for(i=0;i<trial;i++)//枚举下一层搜索中所有单位的 (0--1)横向增量状态:i;
    {
        //计算新编码;
        int flag2=0;//flag==0则新编码合法,flag==1则新编码越界;
        int p=i,j;
        int new_stitus=0;//新状态编码;
        for(j=0;j<time;j++)//对于增量编码 p(from:i),计算新的状态编码 ;
        {
            int increase=p%2;
            if( (increase==0?(step-list[time-j-1]+1):(list[time-j-1]+1))>=(increase==0?N:M) )//是否越界?
            {//如果越界 ;
                flag2=1;//标志越界 ;
                break;
            }
            //转录 ;
            new_stitus*=M;
            new_stitus+=list[time-j-1]+increase;
            p=p/2;
        }//for
        if(flag2==0&&max<temp+fp(step+1,new_stitus))//不越界
        {
            max=temp+fp(step+1,new_stitus);//更新 max 的值(同时进行状态的转移)
        }
    }//for
    move[step][status]=max;//存储此状态的结果 ;
    return max;
}
int main(void)
{
    int i,j;
    scanf("%d%d%d",&time,&M,&N);
    if(M<time)time=M;
    //压缩(当M<time时,更快的方法是直接输出map上所有数的简单相加和,
    //由于数据规模比2S运算量小一个数量级,故这样也可以得满分);
    for(i=0;i<N;i++)
       for(j=0;j<M;j++)
            scanf("%d",&map[i][j]);
    trial=(int)pow(2,time);//0---(trail-1)即所有的0--1状态 ;
    printf("%d",fp(0,0));
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值