背景 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 100∗100000即可。
//提示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;
}