日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

几种常见的动态规划问题模型及优化策略

發布時間:2023/12/19 综合教程 48 生活家
生活随笔 收集整理的這篇文章主要介紹了 几种常见的动态规划问题模型及优化策略 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

斜率優化

  假設一個動態規劃問題的狀態表示為dp[i][j][k]......,通常最外層的幾重循環分別是對i,j,k......等進行遍歷,然后接下來幾重循環(通常只有一重)遍歷對dp[i][j][k]......這個狀態的分解,并取出其中的最優解。斜率優化直觀上的實現方式就是把各種分解方式映射為平面上的點集,一種決策方式對應一條經過點集中一個點的直線,這條直線的斜率是一個跟狀態相關的常數(不同的狀態斜率可能不同),而直線截距的則是該狀態以該種決策方式分解得到的dp值,顯然,這種情況下想得到截距最小的點不用遍歷整個點集,而只要從點集的下凸包中尋找就可以了。因為已經計算完的狀態會加入這個點集之中,所以斜率優化的主要工作就是維護這個下凸包。

  如果狀態轉移方程形式為:dp[i]=min{a[i]*b[j]+c[i]+d[j]},可以將其改寫成:c[i]+d[j]=-a[i]*b[j]+dp[i],將其看成一條直線,c[i]+d[j]是函數值;-a[i]是斜率;b[j]是自變量;dp[i]是截距。

  那么對于狀態dp[i],其分解方式構成的點集為{(b[j],c[i]+d[j])},每次選擇的最優解位于該點集的下凸包上。

  對于下凸包的維護分兩種情況:

  1)點的橫坐標依次遞增:這種情況下只需維護一個隊列。設隊列末尾的兩個元素為k1和k2,即將加入點集的點為k,若k1k2的斜率大于k2k的斜率,那么此時應當刪除k2,重復此步驟直到不滿足條件為止,然后將k加到隊列末尾(因為k橫坐標最大,所以它一定是下凸包中的一員)。

  2)點的橫坐標不滿足遞增:使用Splay或CDQ分治。

  對于已維護好的下凸包,最優的點的選擇也分為兩種情況:

  1)隨著狀態的改變,斜率依次遞增:顯然這種情況下最優的點一定是不斷向右移動的,當滿足當前狀態的斜率大于隊首兩個元素之間的斜率時,刪除隊首元素,重復此步驟直到不滿足條件為止,然后每次直接選擇隊首元素即可。

  2)隨著狀態的改變,斜率不滿足遞增:二分法確定最優點(設斜率為a,最優點為k,則應滿足k-1k之間的斜率小于a,kk+1之間的斜率大于a)

  例題:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<vector>
#include<algorithm>
#include<iostream>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<functional>
#include<math.h>
//#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
typedef vector<int> VI;
typedef pair<int, int> PII;
typedef queue<int> QI;


void makedata() {
    freopen("input.txt", "w", stdout);
    fclose(stdout);
}

int a[1500];
int dp[1500][1500], cost[1500], sum[1500], q[1500];

bool popHead(int k1, int k2, int i, int j) {
    return sum[i] * (sum[k2] - sum[k1]) >
           (dp[k2][j - 1] - cost[k2] + sum[k2] * sum[k2]) - (dp[k1][j - 1] - cost[k1] + sum[k1] * sum[k1]);
}
bool popTail(int k1, int k2, int k, int j) {
    return ((dp[k2][j - 1] - cost[k2] + sum[k2] * sum[k2]) - (dp[k1][j - 1] - cost[k1] + sum[k1] * sum[k1])) * (sum[k] - sum[k2]) >
           ((dp[k][j - 1] - cost[k] + sum[k] * sum[k]) - (dp[k2][j - 1] - cost[k2] + sum[k2] * sum[k2])) * (sum[k2] - sum[k1]);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt", "r", stdin);
#endif
    //makedata();
    std::ios::sync_with_stdio(0), cin.tie(0);
    int n, m;
    while (cin >> n >> m) {
        if (n + m == 0) break;
        m++;
        for (int i = 1; i <= n; i++) cin >> a[i];
        sum[1] = a[1];
        for (int i = 2; i <= n; i++) sum[i] = sum[i - 1] + a[i];
        cost[1] = 0;
        for (int i = 2; i <= n; i++) cost[i] = cost[i - 1] + a[i] * sum[i - 1];
        memset(dp, 0x3F, sizeof(dp));
        for (int i = 1; i <= n; i++) dp[i][i] = 0, dp[i][1] = cost[i];
        for (int j = 2; j <= n; j++) {
            int h = 0, t = 0;
            q[t++] = j - 1;
            q[t++] = j;
            for (int i = j + 1; i <= n; i++) {
                /*for (int k = j - 1; k < i; k++) {
                    dp[i][j] = min(dp[i][j], dp[k][j - 1] + cost[i] - cost[k] - sum[k] * (sum[i] - sum[k]));
                }*/
                while (h + 1 < t && popHead(q[h], q[h + 1], i, j)) h++;
                int k = q[h];
                dp[i][j] = dp[k][j - 1] + cost[i] - cost[k] - sum[k] * (sum[i] - sum[k]);
                while (h + 1 < t && popTail(q[t - 2], q[t - 1], i, j)) t--;
                q[t++] = i;
            }
        }
        cout << dp[n][m] << endl;
    }
    return 0;
}

HDU2829:斜率和橫坐標均單調遞增


總結

以上是生活随笔為你收集整理的几种常见的动态规划问题模型及优化策略的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。