ACM日常訓練日記——8.1(區間dp)

冬天的睡袋發表於2024-08-02
  • 小訓練
  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])

相關文章