DP 水題小結

2021-08-08 17:49:10 字數 4514 閱讀 7376

note:雖然叫做水題,但其實也不簡單。

1.jzoj 5220 c

一道兩個字串的dp。

這樣的題,一般是o(

n2) 的時間複雜度。它的階段一般是兩個串取前i,j個字元的答案,時常還需要根據題意加一些附加的狀態。

這種問題的決策往往也很簡單(決策很簡單不代表很好想到轉移)。一般的思路是a[i] == b[j]a[i] != b[j]出發,在這個分類的基礎上來想狀態轉移方程。實際題目中真正的決策可能是「是否『匹配』a[i]和b[j]」,匹配的含義要看題目。

以這道題為例,狀態肯定是要定義為一般的f[i][j]了,但是怎麼轉移呢?由於題目提到了最長公共子串行,我們還要先把這個東西求出來,設它為lcs[i][j]

注意到,f[i][j]的值其實是與lcs[i][j]相關的,因此轉移時還要考慮到lcs。

如果我們的決策是a[i]不匹配,那麼我們就考慮從f[i - 1][j]轉移過來,但是,只有當lcs[i][j] == lcs[i - 1][j]時,它們匹配的串才是一樣的,才能進行轉移,否則匹配的串完全不同。

如果我們的決策是a[i]去匹配,我們就考慮怎麼轉移。不能從f[i - 1][j - 1]直接轉移到f[i][j],因為串b也是取的乙個子串行。我們應該考慮從上乙個匹配的位置進行轉移。結合之前對lcs的處理,我們可以直到,被轉移的狀態的lcs應該是當前狀態lcs-1。考慮如下狀態:

若下方的染色塊的後面乙個字元(設為x)等於上方的第i個字元,且x+1到j-1中沒有其它滿足等於i的字元,那麼i匹配j的方案數是等於那兩個染色塊匹配的方案數的,即等於i匹配x的方案數。這裡a[i]不一定等於b[j]。但還有個前提,就是之前提到的,兩個染色塊的lcs為當前狀態的lcs-1。

參考**

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

std::cin;

using

std::cout;

using

std::endl;

typedef

int int;

inline int readin()

while (ch >= '0' && ch <= '9')

if (minus) a = -a;

return a;

}const int mod = int(1e9) + 7;

const int maxn = 1005;

int n, m;

char x[maxn], y[maxn];

int readstr(char* buffer)

return length;

}int lcs[maxn][maxn];

int f[maxn][maxn];

void lcs()

else}}

}void dp()

}cout

<< f[n][m] << endl;

}void run()

int main()

2.jzoj 4595 string

又是一道這樣的字串dp。

我們設狀態為f[i][j][k],具體含義應該不用我再說了。

根據經驗,我們肯定還是處理匹配與不匹配的決策,而對於這種多個子串行分組的問題,我們很容易又想到另乙個決策,那就是分組和不分組

注意,做dp一定要想好狀態轉移方程再寫,不要還沒有乙個比較明確的思路就去寫**,否則可能會向乙個完全錯誤的方向去計算。

不多說了,看參考**吧,其中g指f的字首最大值。

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

std::cin;

using

std::cout;

using

std::endl;

typedef

int int;

inline int readin()

while (ch >= '0' && ch <= '9')

if (minus) a = -a;

return a;

}const int maxn = 1005;

int n, m, k;

char s[maxn];

char t[maxn];

int f[maxn][maxn][11];

int g[maxn][maxn][11];

void dp()

g[i][j][k] = std::max(g[i][j][k], f[i][j][k]);

if (k == k) ans = std::max(ans, f[i][j][k]);}}

}cout

<< ans << endl;

}void run()

int main()

3.jzoj 5266 number 數字 dp 第一題

聽說數字dp很久了,但從沒有寫過;也知道了它的大致思路:根據位數劃分階段,然後按照題目要求定義一堆狀態。

可以用記搜的寫法來寫,備忘錄儲存的值為「不頂格」的狀態,所以更新備忘錄時要檢查頂格的標誌。同時需要注意邊界條件的意義。

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

std::cin;

using

std::cout;

using

std::endl;

typedef

unsigned

long

long int;

inline int readin()

while (ch >= '0' && ch <= '9')

if (minus) a = -a;

return a;

}const int maxn = 25;

int n;

int n;

int num[maxn];

int f[maxn][13][2][2];

int dp(int stage, int mod, bool exist, bool isone, bool istop)

if (!istop && ~f[stage][mod][exist][isone]) return f[stage][mod][exist][isone];

int ans = 0;

int maxdigit = istop ? num[stage] : 9;

for (int i = 0; i <= maxdigit; i++)

if (!istop) f[stage][mod][exist][isone] = ans;

return ans;

}void run()

memset(f, -1, sizeof(f));

cout

<< dp(n, 0, false, false, true) << endl;

}int main()

其實質相當於加法原理和乘法原理的應用。

由於是第一次寫,參考的資料也有限,所以數字dp這部分可能以後會有修正!比如,若用遞迴來寫容易爆棧,如果可以用遞推來寫就更好了。

DP水題大禮包

一天8題,6道水題 做法一 考慮在完全揹包上加點東西 做法二 在完全揹包第一次轉移時加值 就是個狀壓dp模板 includeusing namespace std inline int read int n m state 4096 field 13 mod 100000000 dp 13 4096...

Most Powerful(狀壓DP水題)

自己翻譯,注意每次碰撞是兩個中的乙個消失,並不是兩個都消失 dp i 表示i這個狀態最大的能量是多少,三重for迴圈列舉 ac 1 include2 using namespace std 3 define ll long long 4 define inf 0x3f3f3f3f 5 define ...

NYOJ1208 水題系列(DP)

大意 給你乙個有向圖,每條邊都有一定的權值,現在讓你從圖中的任意一點出發,每次走的邊的權值必須必上一次的權值大的情況下,問你最多能走幾條邊?這道題最容易想到的,就是最長單調遞增子串行 但是在這道題上是超時的。每次走到邊的權值必須比上一次的大,所以我們可以先把所有的邊按權值進行從小到大的排序。在定義狀...