ACM日常训练日记——8.1(区间dp)

  • 小训练
  1. 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;
}
  1. 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
  1. 凸多边形的划分
    这道题听不懂思路,但是可以去记住这类题型,多边形的划分三角形顶点的权值乘积和至少为多少。
    我们使用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;
}

  1. 田忌赛马

这道题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;
}

  1. 多个不相交子段和问题

状态转移 ,f[i][j]表示前i个数(第i个数必须取)组成j个不相交子段所能取得的最大和
f[i][j]= max(f[i-1]+a[i],f[k][j-1]+a[i])

posted @ 2024-08-02 02:09  冬天的睡袋  阅读(8)  评论(0编辑  收藏  举报