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

背景 Background

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

题目描述 Description

在一个宽 M ,长 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 时,就相当于是3取方格数。
对于以上的数据:
将输出: 297
//注:如过你想到了无记忆性搜所的方法(不管你怎样优化),你可以直接放弃这道题了。
//提示1:动态规划如果用的是二位数组,规模为 100100000 即可。
//提示2:如果你坚信自己的程序已经无可优化了,可有 2 个数据依然超时,那么告诉你,存在数据有 M<n 的情况!!!

输入 Input

第一行:三个整数: n,M,N
以下的 N 行每行 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 个测试数据,每个测试数据包含 1 个测试点,每个测试点的时间限制为 2 秒钟。
Time Limit : 2s & Memory Limit : 128MB

来源 Source

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

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

题解貌似是记忆化搜索?

#include 
#include 
int move[100][100000]={};//move[step][status];//status是一个 M进制, time位 的状态存储量;
int time,map[100][20],M,N;//横 M <20,竖 N<100,M
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//计算: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//枚举下一层搜索中所有单位的 (0--1)横向增量状态:i;
    {
        //计算新编码;
        int flag2=0;//flag==0则新编码合法,flag==1则新编码越界;
        int p=i,j;
        int new_stitus=0;//新状态编码;
        for(j=0;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&&max1,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//压缩(当M
    //由于数据规模比2S运算量小一个数量级,故这样也可以得满分);
    for(i=0;ifor(j=0;jscanf("%d",&map[i][j]);
    trial=(int)pow(2,time);//0---(trail-1)即所有的0--1状态 ;
    printf("%d",fp(0,0));
    return 0;
}

你可能感兴趣的:(网络流,记忆化搜索)