基础动态规划(递推、计数等)| 做题记录
小编脑子不好使,dp题做得少,被迫加强训练(指做水题)。
洛谷P1026 统计单词个数
令为前i个字符且被划分为了j块的最大单词数
状态转移方程:
其中表示从i到j的单词数
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 10;
std::string s, ch, a[10];
int p, k, m;
int dp[200 + 10][50], sum[200 + 10][200 + 10];
bool check(int l, int r) {
std::string t = ch.substr(l, r - l + 1);
for (int i = 1; i <= m; i++) {
if (t.find(a[i]) == 0) return true;
}
return false;
}
int main() {
scanf("%d%d", &p, &k);
ch += '0';
while (p--) {
std::cin >> s;
ch += s;
}
int len = ch.length() - 1;
scanf("%d", &m);
for (int i = 1; i <= m; i++) std::cin >> a[i];
for (int i = len; i >= 1; i--)//i~j的字符串匹配单词
for (int j = i; j >= 1; j--) {
sum[j][i] = sum[j + 1][i];
if (check(j, i)) sum[j][i]++;
}
dp[0][0] = 0;
for (int i = 1; i <= len; i++)
for (int j = 1; j <= i && j <= k; j++)
for (int q = j - 1; q < i; q++)
dp[i][j] = std::max(dp[i][j], dp[q][j - 1] + sum[q + 1][i]);
printf("%d", dp[len][k]);
}
洛谷P1057 传球游戏
令为传了i次球,当前在第j个人处的方案数
状态转移方程:
注意处理j=1和j=n时的状态转移方程。
#include<iostream>
#include<cstdio>
const int maxn = 30 + 5;
int n, m;
int dp[maxn][maxn];//传了i次,当前在第j个人
int main() {
scanf("%d%d", &n, &m);
dp[0][1] = 1;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++) {
if (j == 1) dp[i][j] = dp[i - 1][2] + dp[i - 1][n];
else if (j == n) dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][1];
else dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
}
printf("%d", dp[m][1]);
}
CF1739C Card Game
令为n张牌时先手必胜的方案数。
所有方案数为,首先计算先手必胜的方案数。
-
我方摸到最大数:必胜,此时剩余牌的方案数是
-
对手摸到最大数:
-
- 对手摸到次大数,此时对手可以用次大数接住我方任何一种牌,下一回合出最大数,必败。
-
- 我方摸到次大数:
-
-
- 对手摸到第三大数。此时我方若出较小的数,对手用第三大数接。下一轮对手出最大数,输;我方若出次大数,对手用最大数接,下一轮对手出第三大数,输。总结:必输。
-
-
-
- 我方摸到第三大数:
-
-
-
-
- 我方摸到第四大数。此时我方出【次大数、第三大数、第四大数】其中一种牌,对手只用最大数接。下一轮对手的牌我方可以剩余两个牌中的一个接,必胜。此时剩余牌方案数为
-
-
-
-
-
- 对手摸到第四大数。此时我方可以用最大数接住任一个数,下一轮我方出第四大叔,对方用剩余的数接,此时四张牌全部打空,问题变为n-4的情况,重复以上步骤
-
-
可以得知,
平局只有一种方案,先手必败的情况用总方案数-平局方案数-先手必胜方案数。
预处理组合数即可。
#include<iostream>
#include<cstdio>
const int mod=998244353;
const int maxn=100;
int T;
int n;
long long C[maxn][maxn],f[maxn];
void init(){
for(int i=0;i<70;i++)
for(int j=0;j<=i;j++){
if(!j) C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
f[2]=1;//有个2
f[4]=3;//有个4
for(int i=6;i<=60;i+=2){
f[i]=(C[i-1][i/2-1]+C[i-4][i/2-3]+f[i-4])%mod;
}
}
//f[i]=C[n/2][3]+C[n/2][1]+f[i-4]
int main(){
scanf("%d",&T);
init();
while(T--){
scanf("%d",&n);
printf("%lld %lld %lld\n",f[n],((C[n][n/2]-f[n]-1)%mod+mod)%mod,1);
}
}
洛谷P1216 Number Triangles
令表示在第层的第个数
状态转移方程:
注意和时的特判就行。
#include<iostream>
#include<cstdio>
const int maxn = 1000 + 10;
int n, a[maxn][maxn], dp[maxn][maxn], ans;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
scanf("%d", &a[i][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++) {
if (j == 1) dp[i][j] = dp[i - 1][j] + a[i][j];
else if (j == i) dp[i][j] = dp[i - 1][j - 1] + a[i][j];
else dp[i][j] = std::max(dp[i - 1][j], dp[i - 1][j - 1]) + a[i][j];
}
for (int i = 1; i <= n; i++) ans = std::max(ans, dp[n][i]);
printf("%d", ans);
return 0;
}
洛谷P1077 摆花
很经典的计数dp+滚动数组优化
令表示选了种花,花总数为时的方案数
状态转移方程:
因为第一维只涉及到和,果断滚动数组降维。
另外这题范围很小,如果达到了那就得参考这题做了...
#include<iostream>
#include<cstdio>
const int mod = 1e6 + 7;
const int maxn = 100 + 10;
int n, m, dp[maxn], a[maxn];
int main() {
dp[0] = 1;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 0; j--)
for (int k = 1; k <= a[i] && k <= j; k++)
dp[j] = (dp[j] + dp[j - k]) % mod;
}
printf("%d", dp[m]);
return 0;
}
洛谷P1115 最大子段和
裸的最大子段和问题。
令为以为结尾的最大子段和。
转移方程:
#include<iostream>
#include<cstdio>
const int maxn = 2e5 + 7;
int a[maxn], n, dp[maxn];
int res = -11451419;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
//res=dp[1]=a[1];
for (int i = 1; i <= n; i++) {
if (dp[i - 1] >= 0) dp[i] = dp[i - 1] + a[i];
else dp[i] = a[i];
res = std::max(res, dp[i]);
}
printf("%d", res);
return 0;
}
洛谷P1095 守望者的逃离
首先预处理仅用魔法跑路的dp状态。
再枚举时间,判断当前走路更优还是按照旧状态更优。
#include<iostream>
#include<cstdio>
const int maxn = 3e5 + 10;
int M, S, T;
int dp[maxn];
bool flag = 0;
int main() {
scanf("%d%d%d", &M, &S, &T);
for (int i = 1; i <= T; i++) {
if (M >= 10) dp[i] = dp[i - 1] + 60, M -= 10;
else M += 4, dp[i] = dp[i - 1];
}
for (int i = 1; i <= T; i++) {
if (dp[i] < dp[i - 1] + 17) dp[i] = dp[i - 1] + 17;
if (dp[i] >= S) {
puts("Yes");
printf("%d", i);
return 0;
}
}
puts("No");
printf("%d", dp[T]);
return 0;
}
NOIP2016 金明的预算方案
在01背包的基础上,增加几个状态,具体为:
1.不拿
2.只拿主件
3.只拿主件+附件1
4.只拿主件+附件2
5.都拿
剩下的就是模板了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 4e4 + 10;
int w[maxn], w2[maxn][5], val[maxn], val2[maxn][5], dp[maxn];
int cnt[maxn];
int n, m;
int v[maxn], p[maxn], q[maxn];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &v[i], &p[i], &q[i]);
if (q[i] == 0) {
cnt[i] = 0;
w[i] = v[i];
val[i] = v[i] * p[i];
} else {
w2[q[i]][++cnt[q[i]]] = v[i];
val2[q[i]][cnt[q[i]]] = v[i] * p[i];
}
}
for (int i = 1; i <= m; i++)
for (int j = n; j >= w[i]; j--) {
dp[j] = std::max(dp[j], dp[j - w[i]] + val[i]);
if (j >= w[i] + w2[i][1]) dp[j] = std::max(dp[j], dp[j - (w[i] + w2[i][1])] + val[i] + val2[i][1]);
if (j >= w[i] + w2[i][2]) dp[j] = std::max(dp[j], dp[j - (w[i] + w2[i][2])] + val[i] + val2[i][2]);
if (j >= w[i] + w2[i][1] + w2[i][2])
dp[j] = std::max(dp[j], dp[j - (w[i] + w2[i][1] + w2[i][2])] + val[i] + val2[i][1] + val2[i][2]);
}
printf("%d", dp[n]);
return 0;
}
#ifdef _DEBUG
for(int i=1;i<=m;i++){
if(!cnt[i]) printf("%d %d\n",w[i][cnt[i]],val[i][cnt[i]]);
else
for(int j=1;j<=cnt[i];j++)
printf("%d %d:%d %d\n",i,j,w[i][j],val[i][j]);
}
#endif
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战