ACM日常训练日记——8.1(区间dp)
- 小训练
- T219724 最大子段和
我本来以为很简单的一道题,用前缀和去找,但是我粗心了一个写错了后面重新写过了,但是,后面发现是一个类似于动态规划的算法题
Kadane 算法
专门求这种最大子段和问题,时间复杂度为O(n)
Kadane算法(Kadane’s Algorithm)是一种用于解决最大子数组和问题(Maximum Subarray Sum Problem)的动态规划算法。该问题的目标是在给定整数数组中找到一个连续的子数组,使其元素之和最大。Kadane算法的时间复杂度为O(n),其中n是数组的长度,因此它是解决这个问题的高效方法。
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
int current_sum = 0;
int max_sum = 0;
int x;
for (int i = 0; i < n; i++) {
cin >> x;
current_sum = max(x, current_sum + x);
max_sum = max(max_sum, current_sum);
}
cout << max_sum << endl;
return 0;
}
前缀和暴力
#include <bits/stdc++.h>
using namespace std;
int v[100861];
int prefix[10005];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>v[i],prefix[i]+=prefix[i-1]+v[i];
int ans=v[1];
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
if(prefix[j]-prefix[i-1]>ans){
ans=prefix[j]-prefix[i-1];
}
}
}
if(ans>=0)
cout<<ans;
else
cout<<0;
}
- Technical Support
用栈存Q,如果遇到不是Q那么弹出Q,最后看stack里面还有没有Q就行
#include <bits/stdc++.h>
using namespace std;
using ll =long long;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
stack<int>st;
for(int i=1;i<=n;i++){
char b;
cin>>b;
if(b=='Q')st.emplace(1);
else {
if(st.size()){
st.pop();
}
}
}
if(st.size()>0)cout<<"No\n";
else cout<<"Yes\n";
}
}
- 动态规划专项训练
- 区间dp
- 凸多边形的划分
这道题听不懂思路,但是可以去记住这类题型,多边形的划分三角形顶点的权值乘积和至少为多少。
我们使用vector来存储权值和DP数组,因为乘积可能会很大。
外层循环len表示当前处理的区间长度,从3开始递增到n。
中层循环i表示区间的起点,范围是1到n-len+1。
内层循环k用于尝试所有可能的分割点。
状态转移方程在内层循环中实现,我们计算分割点k将区间[i,j]分成[i,k]和[k,j]两部分后的总乘积和,并与当前dp[i][j]比较,取最小值。
最终结果存储在dp[1][n]中。
#include <iostream>
#include <vector>
#include <climits>
#include <string>
using namespace std;
// 用于输出 __int128 类型的函数
string to_string(__int128 num) {
if (num == 0) return "0";
string s;
while (num) {
s = char(num % 10 + '0') + s;
num /= 10;
}
return s;
}
// 用于输入 __int128 类型的函数
__int128 read() {
__int128 x = 0;
int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int main() {
int n;
cin >> n;
vector<__int128> w(n + 1);
for (int i = 1; i <= n; i++) {
w[i] = read();
}
vector<vector<__int128>> dp(n + 1, vector<__int128>(n + 1, 0));
for (int len = 3; len <= n; len++) {
for (int i = 1; i + len - 1 <= n; i++) {
int j = i + len - 1;
dp[i][j] = (__int128)1 << 126; // 一个非常大的数
for (int k = i + 1; k < j; k++) {
__int128 temp = dp[i][k] + dp[k][j] + w[i] * w[k] * w[j];
dp[i][j] = min(dp[i][j], temp);
}
}
}
cout << to_string(dp[1][n]) << endl;
return 0;
}
这道题dp可以贪心去做
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 10005;
int a[N], b[N];
signed main() {
ios_base::sync_with_stdio(false); cin.tie(0);
int n; cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
sort(a + 1, a + 1 + n, greater<>());
sort(b + 1, b + 1 + n, greater<>());
int la = 1, ra = n, lb = 1, rb = n, cnt = 0;
for (int i = 1; i <= n; i++) {
if (a[la] > b[lb]) {
cnt++;
la++, lb++;
}
else if (a[ra] > b[rb]) {
cnt++;
ra--, rb--;
}
else if (a[ra] < b[lb]) {
cnt--;
ra--, lb++;
}
}
int ans = cnt * 200;
cout << ans << endl;
}
dp做法,区间dp,每一次齐王都是派出第k大的数,但是田忌不是,田忌如果想要最大那么一开始选择的答案受后面选择的影响,每次选择的k给区间是任意的
//dp[i][j] = max(dp[i+1][j]+cost(i,k), dp[i][j-1]+cost(j,k));
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 10001;
int n;
int dp[maxn][maxn];
int tian[maxn];
int qi[maxn];
int cost(int tian_pos, int qi_pos) {
if (tian[tian_pos]>qi[qi_pos]) return 200;
if (tian[tian_pos]<qi[qi_pos]) return -200;
if (tian[tian_pos]==qi[qi_pos]) return 0;
return 0;
}
signed main() {
cin>>n;
for (int i=1;i<=n;i++) {
cin>>tian[i];
}
for (int i=1;i<=n;i++) {
cin>>qi[i];
}
sort(qi+1, qi+1+n);
sort(tian+1, tian+1+n);
for (int len = 1;len<=n;len++) {
for (int l=1;l+len-1<=n;l++) {
int r = l+len-1;
int k = len-1+1;
dp[l][r] = max(dp[l+1][r]+cost(l, k), dp[l][r-1]+cost(r,k));
}`
}
cout<<dp[1][n];
return 0;
}
状态转移 ,f[i][j]表示前i个数(第i个数必须取)组成j个不相交子段所能取得的最大和
f[i][j]= max(f[i-1]+a[i],f[k][j-1]+a[i])