codeforce#dp专项
Posted iuk11
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codeforce#dp专项相关的知识,希望对你有一定的参考价值。
1 https://codeforces.com/problemset/problem/467/C
【题意】给定一个长度为n的序列,找到k个长度为m的子串(不是子序列),求能得到的每个子串相加后的最大值。特别,子串下标不能重复且不能交叉 。
i
i
i 表示下标
j
j
j 表示子串个数
不新增子串,那最大值与上一次的状态相同;
新增子串,那最大值为以当前下标
i
i
i 结尾的
[
i
−
m
+
1
,
i
]
[i-m+1,i]
[i−m+1,i] 长度的子串,自然
d
p
[
i
−
m
]
[
j
−
1
]
dp[i-m][j-1]
dp[i−m][j−1]为不增加这个子串的最大值,然后再用前缀和加上该区间的值。
两种情况比较取最大(注意第二种情况
i
i
i 的取值范围)
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
−
m
]
[
j
−
1
]
+
b
[
i
]
−
b
[
i
−
m
]
dp[i][j]=max(dp[i-1][j],dp[i-m][j-1]+b[i]-b[i-m]
dp[i][j]=max(dp[i−1][j],dp[i−m][j−1]+b[i]−b[i−m]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5050;
ll a[N],b[N];
ll dp[N][N];
int main()
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
b[i]=b[i-1]+a[i];
int cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
if(i-m>=0)
dp[i][j]=max(dp[i-1][j],dp[i-m][j-1]+b[i]-b[i-m]);
else dp[i][j]=dp[i-1][j];
cout<<dp[n][k]<<endl;
return 0;
2 http://codeforces.com/problemset/problem/706/C
【题意】给定
n
n
n 个字符串,若第
i
i
i 个字符串翻转则需要加上权值
w
i
w_i
wi,若从第一个字符串开始,保证当前字典序一定不比下一个大,问满足此条件,权值和最小是多少。不满足输出-1.
首先想了个离散化,离散化了字符串,错在了前后可能有相同串出现。
因为每个状态只有翻转和不翻转两种情况,翻转权值加
w
i
w_i
wi,则可以得到状态转移方程:
d
p
[
i
]
[
0
]
=
m
i
n
(
d
p
[
i
]
[
0
]
,
d
p
[
i
−
1
]
[
0
]
)
s
[
i
]
≥
s
[
i
−
1
]
d
p
[
i
]
[
0
]
=
m
i
n
(
d
p
[
i
]
[
0
]
,
d
p
[
i
−
1
]
[
0
]
)
s
[
i
]
≥
r
s
[
i
−
1
]
d
p
[
i
]
[
0
]
=
m
i
n
(
d
p
[
i
]
[
0
]
,
d
p
[
i
−
1
]
[
0
]
)
r
s
[
i
]
≥
s
[
i
−
1
]
d
p
[
i
]
[
0
]
=
m
i
n
(
d
p
[
i
]
[
0
]
,
d
p
[
i
−
1
]
[
0
]
)
r
s
[
i
]
≥
r
s
[
i
−
1
]
\\left\\ \\beginarrayrcl dp[i][0]=min(dp[i][0],dp[i-1][0]) & & s[i]\\geq s[i-1]\\\\ dp[i][0]=min(dp[i][0],dp[i-1][0]) & & s[i]\\geq rs[i-1]\\\\ dp[i][0]=min(dp[i][0],dp[i-1][0]) & & rs[i]\\geq s[i-1]\\\\ dp[i][0]=min(dp[i][0],dp[i-1][0]) & & rs[i]\\geq rs[i-1] \\endarray \\right.
⎩⎪⎪⎨⎪⎪⎧dp[i][0]=min(dp[i][0],dp[i−1][0])dp[i][0]=min(dp[i][0],dp[i−1][0])dp[i][0]=min(dp[i][0],dp[i−1][0])dp[i][0]=min(dp[i][0],dp[i−1][0])s[i]≥s[i−1]s[i]≥rs[i−1]rs[i]≥s[i−1]rs[i]≥rs[i−1]
其中
r
s
[
i
]
rs[i]
rs[i] 表示第
i
i
i 个字符串翻转之后的字符串。0代表不翻转,1代表翻转。
在定义正无穷时要定到1e15的级别,这道题的数据有很大的答案。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
typedef long long ll;
const ll INF=1e15;
ll w[N];
string s[N],rs[N];
ll dp[N][2];
string change(string s)
reverse(s.begin(),s.end());
return s;
int main()
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=n;i++)
cin>>s[i];
rs[i]=change(s[i]);
// memset(dp,INF,sizeof(dp));
for(int i=1;i<=n;i++) dp[i][0]=INF,dp[i][1]=INF;
dp[1][0]=0;
dp[1][1]=w[1];
for(int i=2;i<=n;i++)
if(s[i]>=s[i-1]) dp[i][0]=min(dp[i][0],dp[i-1][0]);
if(s[i]>=rs[i-1]) dp[i][0]=min(dp[i][0],dp[i-1][1]);
if(rs[i]>=s[i-1]) dp[i][1]=min(dp[i][1],dp[i-1][0]+w[i]);
if(rs[i]>=rs[i-1]) dp[i][1]=min(dp[i][1],dp[i-1][1]+w[i]);
ll ans=min(dp[n][0],dp[n][1]);
if(ans==INF) cout<<-1<<endl;
else cout<<ans<<endl;
return 0;
以上是关于codeforce#dp专项的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces1582 F2. Korney Korneevich and XOR (hard version)(dp)
Codeforces Round #369 (Div. 2)-C. Coloring Trees DP
Codeforces 1077F1 Pictures with Kittens (easy version)(DP)
Codeforces 1163 DMysterious Code