题目描述
解法:DP(C++)
我们先考虑这样一个问题:如果 t t t 串全是 ?
如样例一
8 6 2 1 1
10110011
????????
这时如果我们看做纸条问题,那么也就是:求一条从 ( 0 , 0 ) (0,0) (0,0)到 ( c n t 0 , c n t 1 ) (cnt0,cnt_1) (cnt0,cnt1)的路径,只能向右(取0)或向下(取1)走,使得路径上的点的权值和最大。
于是,我们就得一个
(
6
+
1
)
×
(
2
+
1
)
(6+1)\times(2+1)
(6+1)×(2+1)的棋盘,左上角记为
(
0
,
0
)
(0,0)
(0,0)
那么,现在的问题是,从左上角走到右下角有多少种走法?好的,是
C
(
8
,
6
)
C(8,6)
C(8,6)种。
那用 6 个 0 和 2 个 1 组成不同的字符串,有多少种?巧了,也是 C ( 8 , 6 ) C(8,6) C(8,6)种。
于是,纸条问题就和这道题对应起来了,这时很容易就想到用动态规划。
记 d p [ x ] [ y ] dp[x][y] dp[x][y]表示 ( x , y ) (x, y) (x,y)的得分, a [ x ] [ y ] a[x][y] a[x][y]表示到达 ( x , y ) (x, y) (x,y)这一步的得分,那么就有:
d p [ x ] [ y ] = m a x ( d p [ x − 1 ] [ y ] , d p [ x ] [ y − 1 ] ) + a [ x ] [ y ] dp[x][y]=max(dp[x−1][y],dp[x][y−1])+a[x][y] dp[x][y]=max(dp[x−1][y],dp[x][y−1])+a[x][y]
对应题目,细化一下转移方程, p o s pos pos表示当前在棋盘的位置对应到字符串的位置, s u m [ x ] sum[x] sum[x] 表示标准串 s s s,到第 x x x 位置为止共有多少个 1, 满足 s s s 与 t t t 前缀 x 1 x_1 x1 相同等价于 s u m [ x 1 ] = = j sum[x_1]==j sum[x1]==j,后缀 x 1 + 1 ~ n x_1+1~n x1+1~n 相同就等价于 s u m [ n ] − s u m [ x 1 ] = = c n t 1 − j sum[n]-sum[x_1]==cnt_1-j sum[n]−sum[x1]==cnt1−j:
①
t
[
p
o
s
]
=
=
0
t[pos]==0
t[pos]==0,即只能右走
m
m
a
x
[
i
]
[
j
]
=
m
a
x
(
m
m
a
x
[
i
]
[
j
]
,
m
m
a
x
[
i
]
[
j
−
1
]
+
v
a
l
p
r
e
∗
(
s
u
m
[
p
o
s
]
=
=
j
)
+
v
a
l
s
u
f
∗
(
s
u
m
[
n
]
−
s
u
m
[
p
o
s
]
=
=
c
n
t
1
−
j
)
)
mmax[i][j]=max(mmax[i][j],mmax[i][j-1]+val_{pre}*(sum[pos]==j)+val_{suf}*(sum[n]-sum[pos]==cnt_1-j))
mmax[i][j]=max(mmax[i][j],mmax[i][j−1]+valpre∗(sum[pos]==j)+valsuf∗(sum[n]−sum[pos]==cnt1−j))
②
t
[
p
o
s
]
=
=
1
t[pos]==1
t[pos]==1,即只能右走
m
m
a
x
[
i
]
[
j
]
=
m
a
x
(
m
m
a
x
[
i
]
[
j
]
,
m
m
a
x
[
i
−
1
]
[
j
]
+
v
a
l
p
r
e
∗
(
s
u
m
[
p
o
s
]
=
=
j
)
+
v
a
l
s
u
f
∗
(
s
u
m
[
n
]
−
s
u
m
[
p
o
s
]
=
=
c
n
t
1
−
j
)
)
mmax[i][j]=max(mmax[i][j],mmax[i-1][j]+val_{pre}*(sum[pos]==j)+val_{suf}*(sum[n]-sum[pos]==cnt_1-j))
mmax[i][j]=max(mmax[i][j],mmax[i−1][j]+valpre∗(sum[pos]==j)+valsuf∗(sum[n]−sum[pos]==cnt1−j))
③
t
[
p
o
s
]
=
=
?
t[pos]==?
t[pos]==?,即可右可下
m
m
a
x
[
i
]
[
j
]
=
m
a
x
(
m
m
a
x
[
i
−
1
]
[
j
]
,
m
m
a
x
[
i
]
[
j
−
1
]
)
+
v
a
l
p
r
e
∗
(
s
u
m
[
p
o
s
]
=
=
j
)
+
v
a
l
s
u
f
∗
(
s
u
m
[
n
]
−
s
u
m
[
p
o
s
]
=
=
c
n
t
1
−
j
)
mmax[i][j]=max(mmax[i-1][j],mmax[i][j-1])+val_{pre}*(sum[pos]==j)+val_{suf}*(sum[n]-sum[pos]==cnt_1-j)
mmax[i][j]=max(mmax[i−1][j],mmax[i][j−1])+valpre∗(sum[pos]==j)+valsuf∗(sum[n]−sum[pos]==cnt1−j)
情况③可以由情况①②表出
最后就是编程上的一些细节:
s = " "+s; t = " "+t;
我们在两个字符串前加入空格的原因和棋盘大小的生成原理是一样的,让下标从 1 开始才是我们处理的内容mmax[0][0] = mmin[0][0] = vs*(sum[n]==cnt1);
初始化时如果两字符串后缀 1 ~ n 1~n 1~n 是相同的,要计算上这一部分
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3+10;
int n, cnt0, cnt1, vp, vs, pos, mmax[N][N], mmin[N][N], sum[N];
string s, t;
int main()
{
cin >> n >> cnt0 >> cnt1 >> vp >> vs;
cin >> s >> t;
s = " "+s;
t = " "+t;
for(int i=1;i<=n;i++)
sum[i] = sum[i-1]+(s[i]-'0');
memset(mmax, -0x3f, sizeof(mmax));
memset(mmin, 0x3f, sizeof(mmin));
mmax[0][0] = mmin[0][0] = vs*(sum[n]==cnt1);
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
pos = i+j;
if(t[pos]!='0'&&j)
{
mmax[i][j] = max(mmax[i][j], mmax[i][j-1]+vp*(sum[pos]==j&&pos)+vs*(sum[n]-sum[pos]==cnt1-j&&pos!=n));
mmin[i][j] = min(mmin[i][j], mmin[i][j-1]+vp*(sum[pos]==j&&pos)+vs*(sum[n]-sum[pos]==cnt1-j&&pos!=n));
}
if(t[pos]!='1'&&i)
{
mmax[i][j] = max(mmax[i][j], mmax[i-1][j]+vp*(sum[pos]==j&&pos)+vs*(sum[n]-sum[pos]==cnt1-j&&pos!=n));
mmin[i][j] = min(mmin[i][j], mmin[i-1][j]+vp*(sum[pos]==j&&pos)+vs*(sum[n]-sum[pos]==cnt1-j&&pos!=n));
}
}
}
cout << mmin[cnt0][cnt1] << " " << mmax[cnt0][cnt1] << endl;
return 0;
}