奖学金
抱歉,题目表述有误,导致一些同学读错题目了。但是审核又通过了就改不了题目内容了。
抱歉抱歉!
输入的内容应该是接下来 1 − n 1 - n 1−n行 ,每行有 1 到 m 个 数 1到m个数 1到m个数
常规解法1:动态规划
时间复杂度
O
(
N
4
)
O(N^4)
O(N4)
一个人时
对于单人,一个经典的题目:走方格
我们知道的DP做法,
f
(
i
,
j
)
=
m
a
x
{
f
(
i
,
j
−
1
)
,
f
(
i
−
1
,
j
)
}
+
a
[
i
]
[
j
]
f(i,j)=max\{f(i,j-1),f(i-1,j)\}+a[i][j]
f(i,j)=max{f(i,j−1),f(i−1,j)}+a[i][j]
两个人时
那么对于两个人的情况,我们可以加多两个维度,表示另外一个人的走路情况
f
(
i
,
j
,
k
,
l
)
f(i,j,k,l)
f(i,j,k,l) 然后枚举每个人的走路情况,类似第一种做法
f
[
i
]
[
j
]
[
k
]
[
l
]
=
m
a
x
(
f
[
i
]
[
j
−
1
]
[
k
]
[
l
−
1
]
,
f
[
i
]
[
j
−
1
]
[
k
−
1
]
[
l
]
,
f
[
i
−
1
]
[
j
]
[
k
−
1
]
[
l
]
,
f
[
i
−
1
]
[
j
]
[
k
]
[
l
−
1
]
)
+
a
[
i
]
[
j
]
+
a
[
k
]
[
l
]
;
f[i][j][k][l] = max(f[i][j-1][k][l-1],f[i][j-1][k-1][l],f[i-1][j][k-1][l],f[i-1][j][k][l-1])+a[i][j]+a[k][l];
f[i][j][k][l]=max(f[i][j−1][k][l−1],f[i][j−1][k−1][l],f[i−1][j][k−1][l],f[i−1][j][k][l−1])+a[i][j]+a[k][l];
但是空间,时间复杂度可以优化
定义
f
(
p
,
i
,
j
)
f(p,i,j)
f(p,i,j)表示当前走了
p
p
p步,第一个人走到第
i
i
i行,第二个人走到第j行的最大价值。
显然两个人的坐标都可以计算出来,第一个人是 ( i , p − i + 1 ) (i,p-i+1) (i,p−i+1),第二个人是 ( j , p − j + 1 ) (j,p-j+1) (j,p−j+1)。
转移就考虑两个人的上一步是怎样走的。
f
[
p
]
[
i
]
[
j
]
=
m
a
x
(
m
a
x
(
f
[
p
−
1
]
[
i
]
[
j
]
,
f
[
p
−
1
]
[
i
−
1
]
[
j
]
)
,
m
a
x
(
f
[
p
−
1
]
[
i
]
[
j
−
1
]
,
f
[
p
−
1
]
[
i
−
1
]
[
j
−
1
]
)
)
+
a
[
i
]
[
p
−
i
+
1
]
+
a
[
j
]
[
p
−
j
+
1
]
f[p][i][j] = max(max(f[p - 1][i][j], f[p - 1][i - 1][j]), max(f[p - 1][i][j - 1], f[p - 1][i - 1][j - 1])) + a[i][p - i + 1] + a[j][p - j + 1]
f[p][i][j]=max(max(f[p−1][i][j],f[p−1][i−1][j]),max(f[p−1][i][j−1],f[p−1][i−1][j−1]))+a[i][p−i+1]+a[j][p−j+1]
三个人时
f
(
i
,
j
,
k
,
u
)
f(i,j,k,u)
f(i,j,k,u)表示第
i
i
i步时,三个人所在的行数分别为
j
,
k
,
u
j,k,u
j,k,u
三个人的坐标为
(
j
,
j
−
i
+
1
)
,
(
k
,
k
−
i
+
1
)
,
(
u
,
u
−
i
+
1
)
(j, j - i + 1), (k, k - i + 1),(u, u - i + 1)
(j,j−i+1),(k,k−i+1),(u,u−i+1)
从左边或者下边走来,
2
×
2
×
2
2\times 2\times 2
2×2×2,一共8种可能,类似上面的情况,具体见代码
code:
#include<bits/stdc++.h>
using namespace std;
const int N = 125, M = 65;
int f[N][M][M][M], g[M][M], n, m;
int main() {
cin >> n >> m;
int sum = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
cin >> g[i][j];
}
int i = 1, j = n;
for (int i = 1; i <= n + m; i++) {
for (int j = 1; j <= n && j <= i; j++) {
for (int k = 1; k <= n && k <= i; k++) {
for (int u = 1; u <= n && u <= i; u++) {
int w = g[j][i - j + 1];
if (k != j) w += g[k][i - k + 1];
if (u != k && j != u) w += g[u][i - u + 1];
int maxv = 0;
maxv = max(f[i - 1][j][k - 1][u], maxv);
maxv = max(f[i - 1][j][k][u - 1], maxv);
maxv = max(f[i - 1][j - 1][k][u], maxv);
maxv = max(f[i - 1][j - 1][k - 1][u], maxv);
maxv = max(f[i - 1][j - 1][k][u - 1], maxv);
maxv = max(f[i - 1][j][k - 1][u - 1], maxv);
maxv = max(f[i - 1][j - 1][k - 1][u - 1], maxv);
maxv = max(f[i - 1][j][k][u], maxv);
f[i][j][k][u] = max(f[i][j][k][u], maxv + w);
}
}
}
}
cout << f[n + m][n][n][n] << endl;
return 0;
}
骚操作:图论建模(网络流)
但是我们发现这个题目如果拓展成k个人的情况那该怎么办啊,用动态规划的话,状态又很繁琐和空间不太够,时间直接上升到
O
(
n
k
)
O(n^k)
O(nk)了
这个时候就需要一点图论的功底了,接下来我们将如何用
M
2
l
o
g
N
M^2logN
M2logN解决此类问题的。
前置知识
如何将这个题目转化为一个网络流题目呢
对于一个格子我们只能取一个数对吧,也就是说取完了就不能再取了,但是队友还得路过那里啊。怎么办呢?
我们将格子拆分成两个,具体来说就是将改点分裂成两个,一个是开始取得点(入点),一个是去后的点(出点),将该点的权值变为连边费用,因为只能一个人取啊,所以我们将该入点与出点之间的之间额的流量改为
1
1
1
那该格子走到另外一个格子呢,我们可以将该点的出点与另外一个入点连边,费用为
0
0
0,流量为无穷(因为可以重复走,代价为0),源点与第一个点连代价为
0
0
0的,流量为
k
k
k的边,汇点与第一个点连代价为
0
0
0的,流量为
k
k
k的边.
跑一下最大费用最大流即可啦。
对于网络流模板不难,建模难!
建模后类似此图(图画得很丑,不完整,见谅,主要表达思想)
跑一下网络最大费用最大流即可
code:
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+100;
int head[N],ver[N],nex[N],edge[N],cost[N];
int tot=1;
void addedge(int x,int y,int z,int c){
ver[++tot]=y,nex[tot]=head[x],cost[tot]=c;
edge[tot]=z,head[x]=tot;
}
void add(int x,int y,int z,int c){
addedge(x,y,z,c);
addedge(y,x,0,-c);
}
//以下费用流基操,不介绍
int maxflow=0,maxcost=0;
int n,m,s,t;
int d[N];
int in[N];
int incf[N];
int pre[N];
bool spfa(){
queue<int> q;
memset(d,0xef,sizeof(d));
memset(in,0,sizeof(0));
q.push(s);
d[s]=0,in[s]=1;
incf[s]=INF;
while(!q.empty()){
int x=q.front();q.pop();
in[x]=0;
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(!edge[i]) continue;
if(d[y]<d[x]+cost[i]){
d[y]=d[x]+cost[i];
incf[y]=min(incf[x],edge[i]);
pre[y]=i;
if(!in[y]) q.push(y),in[y]=1;
}
}
}
if(d[t]==0xefefefef) return 0;
return 1;
}
void update(){
int x=t;
while(x!=s){
int i=pre[x];
edge[i]-=incf[t];
edge[i^1]+=incf[t];
x=ver[i^1];
}
maxflow+=incf[t];
maxcost+=incf[t]*d[t];
}
int num(int i,int j){
return (i-1)*m+j;
}
int main(){
int k=3; //三个人的情况
cin>>n>>m;
s=3*n*m;t=s+1; //源点,汇点
int p=n*m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int x;
cin>>x;
add(num(i,j),num(i,j)+p,1,x);
add(num(i,j),num(i,j)+p,INF,0);
if(i+1<=n) add(num(i,j)+p,num(i+1,j),INF,0);
if(j+1<=m) add(num(i,j)+p,num(i,j+1),INF,0);
}
}
add(s,num(1,1),k,0);
add(num(n,m)+p,t,k,0);
while(spfa()) update();
cout<<maxcost<<endl;
return 0;
}