102400118 林嘉祚 寒假集训第六专题
AC截图:
1、最大子段和
dp[i]表示选到第i个数字时的最大和。状态由当前数字选或不选决定,选则是dp[i-1]+a[i],不选则是a[i],取max即可。
#include <iostream>
using namespace std;
int arr[200010];
int dp[200010];
int main()
{
int n;
cin >> n;
for (int i = 1 ; i <= n ; i++)
{
scanf("%d",&arr[i]);
}
for (int i = 1 ; i <= n ; i++) //头
{
dp[i] = max(dp[i-1]+arr[i],arr[i]);
}
int maxn = -1e9;
for (int i = 1 ; i <= n ; i++)
{
maxn = max(maxn,dp[i]);
}
cout << maxn << endl;
}
2、采药
经典的01背包问题,状态同样由选和不选转移
#include <iostream>
using namespace std;
int f[110][1010];
int main()
{
int t[110];
int w[110];
int T,m;
cin >> T >> m;
for (int i = 1 ; i <= m ; i++)
{
cin >> t[i] >> w[i];
}
for (int i = 1 ; i <= m ; i++)
{
for (int j = 0 ; j <= T ; j++)
{
if (j < t[i])
{
f[i][j] = f[i-1][j];
}
else
{
f[i][j] = max(f[i-1][j],f[i-1][j-t[i]]+w[i]);
}
}
}
cout << f[m][T] << endl;
return 0;
}
3、宝物筛选
多重背包模版题,用二进制优化枚举过程
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
const int M = 4e4 + 10;
long long dp[M];
int v[N]; //价值
int w[N]; //重量
int main()
{
int n,W;
cin >> n >> W;
int cnt = 0;
for (int i = 1 ; i <= n ; i++)
{
int v1,w1,m1;
cin >> v1 >> w1 >> m1;
int t = 1;
while (m1 >= t)
{
cnt++;
v[cnt] = v1 * t;
w[cnt] = w1 * t;
m1 -= t;
t <<= 1;
}
if (m1 > 0)
{
cnt++;
v[cnt] = v1 * m1;
w[cnt] = w1 * m1;
}
}
for (int i = 1 ; i <= cnt ; i++) //枚举物品
{
for (int j = W ; j >= 0 ; j--)
{
if (j >= w[i])
{
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}
}
}
cout << dp[W] << endl;
return 0;
}
4、最长公共子序列
朴素的求LCS做法O(n^2)一定超时,考虑优化。求LCS问题可以转化成在1个数组中求LIS。首先将a数组元素顺序标号,同时将这些标号映射到b数组中,问题就转化成了在这个映射后的新数组中求LIS。
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N],a1[N];
int b[N];
int dp[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
for (int i = 1 ; i <= n ; i++)
{
cin >> a[i];
a1[a[i]] = i;
}
for (int i = 1 ; i <= n ; i++) cin >> b[i];
int len = 0;
for (int i = 1 ; i <= n ; i++)
{
int l = 0;
int r = len + 1;
while (l+1 != r)
{
int mid = (l+r) >> 1;
if (a1[b[i]] > dp[mid]) l = mid;
else r = mid;
}
if (r > len)
{
len++;
dp[len] = a1[b[i]];
}
else dp[r] = min(dp[r],a1[b[i]]);
}
cout << len << endl;
return 0;
}
5、Kevin and Puzzle
初见时并不会做,听完讲解才补完。
本题关键在于两个说谎的人不会站在一起。所以i位置为骗子时,只可能由i-1位置为真话转移来。难点在于i位置为真话时,考虑由i-1位置为真话或为假话转移,讨论转移满足的条件。若i-1位置为真话时,a[i]必须与a[i-1]相等;若i-1位置为假话时,则i-2必定是真话,此时需要满足a[i]=a[i-2]+1。
#include <iostream>
#include <cstring>
using namespace std;
const int MOD = 998244353;
const int N = 2e5 + 10;
int a[N];
int dp[N][2]; //0诚实,1说谎
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
for (int i = 1 ; i <= n ; i++) cin >> a[i];
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for (int i = 1 ; i <= n ; i++)
{
dp[i][1] = dp[i-1][0];
if (a[i] == a[i-1])
{
dp[i][0] += dp[i-1][0];
dp[i][0] %= MOD;
}
if (i-2 >= 0 && a[i-2]+1 == a[i])
{
dp[i][0] += dp[i-2][0];
dp[i][0] %= MOD;
}
}
cout << (dp[n][0]+dp[n][1]) % MOD << endl;
}
return 0;
}
6、World is Mine
本题拿到首先考虑二人都会以什么策略吃蛋糕。显然,Alice一定是从还存有的比上一个吃的大的蛋糕里,找到最小的那个。至于Bob,一开始可能认为Bob吃数量最少的蛋糕是最优解。但是由于吃蛋糕具有时效性,因此这种贪心做法并不正确,只能使用dp。
记dp[i][j]为前i个蛋糕,Bob吃了j个,Bob能消除的蛋糕种数。Bob吃掉一种蛋糕的代价为该蛋糕个数+1,因为若消除了此种蛋糕,Bob会在吃到i时失去一次吃蛋糕机会。于是问题就转化成了背包dp
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 5010;
int a[N];
int cnt[N];
int dp[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n;
memset(cnt,0,sizeof(cnt));
for (int i = 1 ; i <= n ; i++)
{
cin >> a[i];
cnt[a[i]]++;
}
sort(a+1,a+n+1);
int end = unique(a+1,a+n+1) - a - 1;
for (int i = 1 ; i <= end ; i++)
{
for (int j = 1 ; j <= i ; j++)
{
dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
if (j >= cnt[a[i]]+1)
{
dp[i][j] = max(dp[i][j],dp[i-1][j-cnt[a[i]]-1]+1);
}
// cout << dp[i][j] << " ";
}
// cout << endl;
}
cout << end - dp[end][end] << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】