1. 程式人生 > >讀書筆記 - 其他經典動態規劃問題

讀書筆記 - 其他經典動態規劃問題

規模 names padding pac %d can 子序列 計算 化簡

當然,還有LIS, 不過之前總結過了,這次就不貼了

/**
給定兩個字符串s1s2s3...sn和t1t2t3...tm
求這兩個字符串最長的公共子序列的長度

dp[i][j]表示s序列考慮si,t序列考慮tj時的最長公共子序列

狀態轉移方程:
s[i] == t[j] : dp[i][j] = dp[i-1][j-1] + 1
s[i] != t[j] : dp[i][j] = max(dp[i-1][j], dp[i][j-1])
*/

#include <iostream>
#include <stdio.h>
#include <string.h>
using
namespace std; const int MAXN = 1000 + 5; char s[MAXN], t[MAXN]; int dp[MAXN][MAXN]; int solve() { scanf("%s%s", s + 1, t + 1); int n = strlen(s + 1); int m = strlen(t + 1); memset(dp, 0, sizeof(dp)); for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) {
if(s[i] == t[j]) dp[i][j] = dp[i-1][j-1] + 1; else dp[i][j] = max(dp[i][j-1], dp[i-1][j]); } } printf("%d\n", dp[n][m]); return dp[n][m]; } int main() { solve(); return 0; }

/**
有n種不同大小的數字,每種mi個,判斷是否可以從這些數字中選出若幹個
讓他們的和恰好為K

問題規模:
 1 <= n <= 100
 1 <= ai,mi <= 1e5
 1 <= K <= 1e5

 思路一:
 dp[i][j] 表示前i種物品挑選若幹個是否可以組成和是j
 狀態轉移方程: dp[i][j] = dp[i][j] | dp[i-1][j - k*ai] ( j >= k*ai, 0 <= k <= mi)
 復雜度O(K*sum(mi)) 還是比較高的

 思路二:
 dp[i][j] 表示前i中物品加和得到j的情況下,mi個ai最多能剩多少個,如果不能得到j,值為-1
 狀態轉移方程:

 dp[i][j] = mi, if  dp[i-1][j] >= 0
 dp[i][j] = -1, if  j < ai || dp[i][j - ai] <= 0
 dp[i][j] = dp[i][j - ai] + 1, else ...

 復雜度是O(nK)
*/ #include <iostream> #include <stdio.h> #include <string.h> using namespace std; const int MAXK = 1e5 + 5; const int N = 100 + 5; int n, K; int m[N], a[N]; int dp1[N][MAXK]; int dp2[MAXK]; void solve1() { memset(dp1, 0, sizeof(dp1)); dp1[0][0] = 1; for(int i = 1; i <= n; i++) { for(int j = 0; j <= K; j++) { for(int k = 0; k <= m[i]; k++) { if(j - k * a[i] >= 0) dp1[i][j] |= dp1[i-1][j - k * a[i]]; } } } if(dp1[n][K]) printf("Yes\n"); else printf("No\n"); } void solve2() { memset(dp2, -1, sizeof(dp2)); dp2[0] = 0; for(int i = 1; i <= n; i++) { for(int j = 0; j <= K; j++) { if(dp2[j] >= 0) dp2[j] = m[i]; else if(j < a[i] || dp2[j - a[i]] <= 0) dp2[j] = -1; else dp2[j] = dp2[j - a[i]] + 1; } } if(dp2[K] >= 0) printf("Yes\n"); else printf("No\n"); } int main() { scanf("%d%d", &n, &K); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int j = 1; j <= n; j++) scanf("%d", &m[j]); solve1(); //solve2(); return 0; }

/**
有n個無區別的物品,將它們劃分成不超過m組,求劃分方法的個數,對M取模
問題規模:
1 <= m <= n <= 1000
2 <= M <= 10000

DP方法:
dp[i][j] 表示j的i劃分.
j的i劃分,將j劃分成A1,A2,...,Ai,那麽在每一個A中取出一個,就變成了
j-i的i劃分。
如果Ak == 0,那麽就成了j的i-1劃分了
所以有遞推式
dp[i][j] = dp[i][j-i] + dp[i-1][j];
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int N = 1000 + 5;
int n, m;
int dp[N][N];

void solve()
{
    dp[0][0] = 1;
    for(int i = 1; i <= m; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            if(j - i >= 0)
                dp[i][j] = dp[i-1][j];
            else
                dp[i][j] = (dp[i-1][j] + dp[i][j-i]) % M;
        }
    }
    printf("%d\n", dp[m][n]);
}

int main()
{
    scanf("%d%d", &n, &m);
    solve();
    return 0;
}

/**
多重集組合數問題
有n種物品,第i種物品有ai個。不同種類的物品可以相互區分,但是同種類的物品是無法區分的
從這些物品中取出m個的話,有多少種取法,方法數模M
問題規模:
1 <= n <= 1000
1 <= m <= 1000
1 <= ai <= 1000
2 <= M <= 10000

//本題的方法學到了一種化簡的思路
動態規劃法:
dp[i][j] 表示的是考慮第i種物品,取出了j個的取法數
得到狀態轉移方程:
dp[i][j] = sum(dp[i-1][j-k]) ,0 <= k <= ai 且 j >= k
當然,這樣的轉移方程很容易得到,求解也很容易,但是需要三重循環。
時間復雜度是O(sum(ai)*m),這題應該計算量大概是1e9的數量級,有可能會卡過,但是很玄
這個時候需要來化簡這個轉移方程
sum(dp[i-1][j-k]) , 0 <= k <= min(j, ai)
sum(dp[i-1][j-k]) = sum(dp[i-1][j-1 - k]) + dp[i-1][j] - dp[i-1][j-1 - ai], 0 <= k <= min(j-1, ai)
這樣就有 dp[i][j] = dp[i][j-1] + dp[i-1][j] - dp[i-1][j-1 - ai]
復雜度就變成O(nm),完全可以應付
*/
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;

const int N = 1000 + 5;
const int M = 1000 + 5;

int n, m;
int a[N];
int dp[N][MAXM];
int M;

int read()
{
    scanf("%d%d%d", &n, &m, &M);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
}

void solve()
{
    for(int i = 0; i <= n; i++)
        dp[i][0] = 1;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= m; j++)
        {
            if(j - 1 -a[i] >= 0)
                dp[i][j] = (dp[i][j-1] + dp[i-1][j] - dp[i-1][j - 1 - a[i]] + M) % M;
            else
                dp[i][j] = (dp[i][j-1] + dp[i-1][j]) % M;
        }
    }
    printf("%d\n", dp[n][m]);
}

int main()
{
    solve();
    return 0;
}

讀書筆記 - 其他經典動態規劃問題