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