天天看點

第七次集訓——常見動态規劃模型(清明)

  模型一:石子歸并類

題目一:Northeastern Europe 2001, Far-Eastern Subregion

/*******************************************************

** Description: 給定N張卡片,每次隻能從中間取卡片,

取走一張卡片的價值為這張卡片的價值和相鄰的卡片價值的乘積,

求最後剩下2張卡片時,能獲得的最小價值是多少?

** Analysis: f[i][j] 表示 從 第i張 到 第j張,能或得的最小價值

階段: 取掉的連續區間的長度

決策: f[i][j] = min(f[i+1][j]+w[i-1]*w[i]*w[j+1],

f[i][j-1]+w[j+1]*w[j]*w[i-1],

f[i][k-1]+f[k+1][j]+w[k]*w[i-1]*w[j+1]), i < k < j

k表示在這個區間中最後取走的牌的編号

** Time: O(N^3)

*******************************************************/

#include <iostream>

using namespace std;

const int maxn = 101;

int a[maxn];

long long f[maxn][maxn];

int n;

long long min(long long a, long long b) {

if (a < b) return a; else return b;

}

int main() {

scanf("%d", &n);

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

for (int i = 2; i < n; i++) f[i][i] = a[i-1]*a[i]*a[i+1];

for (int k = 2; k <= n-2; k++)

for (int i = 2; i <= n-k; i++) {

int j = i+k-1;

f[i][j] = min(f[i+1][j]+a[i-1]*a[i]*a[j+1], f[i][j-1]+a[j+1]*a[j]*a[i-1]);

for (int p = i+1; p < j; p++)

f[i][j] = min(f[i][j], f[i][p-1]+f[p+1][j]+a[p]*a[i-1]*a[j+1]);

}

printf("%I64d/n", f[2][n-1]);

return 0;

}

模型二:同餘類

題目一:Northeastern Europe 1999

/*****************************************************************

** Description: 給定N個數,求有這N個數加減能否得到某一個數P的倍數?

** Analysis: f[i][j] 表示用前i個數能否得到模P餘J

f[i][j] = f[i-1][(j-w[i])modP] || f[i-1][(j+w[i])modP]

但要注意 % 操作對于負數的情況 -1 % 5 = -1

** Time: O(P*N)

*****************************************************************/

#include <iostream>

using namespace std;

int n, p, m, state;

int h[2][101];

int f(int k) {

k = k % p;

k = (k+p)%p;

return k;

}

int main() {

while (scanf("%d%d", &n, &p) == 2) {

memset(h,0,sizeof(h));

state = 0; h[0][0] = 1;

for (int i = 0; i < n; i++) {

scanf("%d", &m);

state = 1 - state;

for (int j = 0; j < p; j++) h[state][j] = 0;

for (int j = 0; j < p; j++)

if (h[1-state][j]) h[state][f(j+m)] = h[state][f(j-m)] = 1;

}

if (h[state][0]) printf("Divisible/n"); else printf("Not divisible/n");

}

return 0;

}

模型三:最長不下降子序類

題目一: Waterloo local 2000.09.23

/*****************************************************************

** Description: 按字典序給出N個單詞,對一個單詞可以進行修改一個字母,添加一個字母,删除一個字母的操作

求出做多選出多少個單詞,使得相鄰兩個滿足進行一次操作

** Analysis: 每次構造出由但前單詞可以構造出的所有單詞,然後去判斷前面是否出現過,找出一個最大值

算法1:trie 算法2:二分

*****************************************************************/

#include <iostream>

#include <string.h>

using namespace std;

const int maxn = 25010;

int n, len, k;

int f[maxn];

char w[maxn][20], s[20];

int check() {

int l = 1, r = n, mid;

while (l+1<r) {

mid = (l+r)/2;

if (strcmp(s, w[mid]) >= 0) l = mid; else r = mid;

}

if (strcmp(w[l], s) == 0) return l; else return 0;

}

int main() {

freopen("e.in", "r",stdin);

freopen("e.out", "w", stdout);

n = 0; f[0] = 0;

while (scanf("%s", w[++n]) == 1) {

len = strlen(w[n]); f[n] = 1;

//cout << w[n] << endl;

if (n == 1) continue;

//changing

for (int i = 0; i < len; i++) s[i] = w[n][i]; s[len] = '/0';

for (int i = 0; i < len; i++) {

for (char j = 'a'; j < w[n][i]; j++) {

s[i] = j;

//cout << s << endl;

k = check();

if (f[k]+1>f[n]) f[n] = f[k]+1;

}

s[i] = w[n][i];

}

//delete

s[len-1] = '/0';

if (len > 1)

for (int i = 0; i < len; i++) {

for (int j = 0; j < i; j++) s[j] = w[n][j];

for (int j = i+1; j < len; j++) s[j-1] = w[n][j];

//cout << s << endl;

len--; k = check(); len++;

if (f[k]+1>f[n]) f[n] = f[k]+1;

}

//addition

s[len+1] = '/0';

for (int i = 0; i < len; i++) s[i+1] = w[n][i];

for (int i = 0; i < len; i++) {

len++;

for (char j = 'a'; j <= w[n][i]; j++) {

s[i] = j;

//cout << s << endl;

k = check();

if (f[k]+1>f[n]) f[n] = f[k]+1;

}

len--;

s[i] = w[n][i];

}

}

int max = 0;

for (int i = 1; i <= n; i++)

if (f[i] > max) max = f[i];

printf("%d/n", max);

return 0;

}

題目二:Northeastern Europe 1998

/***************************************************

** Desrtiption: 有N個賊,每個賊在某一個時刻Ti來到商店,商店有有0--K的門,

隻有目前商店門的編号與賊心中的編号Si附合時才會偷價值為Pi的物品

在一個機關時間内門的編号可以就+1 0 -1

求在可以操作門的情況下,最多能偷走物品數?

** Analysis: 來的時間有先後,排個序!

f[i] 表示以第i人偷時的最優值,那麼

f[i] = max(f[j]+p[i]), |s[i]-s[j]|<=|t[i]-t[j]|, 1 <= j < i

** Time: O(N^2)

/***************************************************

#include <iostream>

using namespace std;

const int maxn = 101;

int t[maxn], p[maxn], s[maxn], f[maxn];

int n, k, tt;

void swap(int &a, int &b) {

int t = a; a = b; b = t;

}

int main() {

freopen("g.in", "r", stdin);

scanf("%d%d%d", &n, &k, &tt);

for (int i = 1; i <= n; i++) scanf("%d", &t[i]);

for (int i = 1; i <= n; i++) scanf("%d", &p[i]);

for (int i = 1; i <= n; i++) scanf("%d", &s[i]);

//sort

for (int i = 1; i < n; i++)

for (int j = i+1; j <= n; j++)

if (t[j] < t[i]) {

swap(t[i], t[j]);

swap(p[i], p[j]);

swap(s[i], s[j]);

}

t[0] = 0; s[0] = 0; f[0] = 0;

for (int i = 1; i <= n; i++) {

f[i] = -1 << 30;//necessary

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

if (abs(s[i]-s[j]) <= abs(t[i]-t[j]))

if (f[j] + p[i] > f[i])

f[i] = f[j] + p[i];

}

int max = 0;

for (int i =1; i <= n; i++)

if (f[i] > max) max = f[i];

printf("%d/n", max);

return 0;

}

模型四:背包類

NUDT-P2180

/******************************************

** Description: 給定N個物品, 給出這N個物品之間的關系,有直接關系的不能放進同一堆

求把這N個物品放進兩堆最小內插補點是多少?

** Analysis: 初看好像和背包沒有關系???對于一個連通分量轉化成二分圖後,兩堆的內插補點是确定的

是以可以把這個內插補點作為一個物品的重量去做,轉化為M個物品分成兩堆的最小差距,就可以用背包完成了

********************************************/

#include <iostream>

using namespace std;

const int maxn = 100, maxv = 2010;

int n, i, j, now, l, r, sum, c[maxn];

bool d[maxn][maxn], h[maxv], v[maxn];

char map[110];

void dfs(int x, int sgn) {

now += sgn*c[x];

v[x] = 1;

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

if (!v[i] && d[x][i]) dfs(i, -sgn);

}

int main() {

freopen("d.in", "r", stdin);

while (scanf("%d", &n) == 1) {

for (i = 0; i < n; i++) scanf("%d", &c[i]);

for (i = 0; i < n; i++) {

scanf("%s", map);

for (j = 0; j < i; j++)

if (map[j] == '1')

d[i][j] = d[j][i] = 1;

else

d[i][j] = d[j][i] = 0;

}

memset(v,0,sizeof(v));

memset(h,0,sizeof(h));

h[0] = 1; sum = 0;

for (i = 0; i < n; i++)

if (!v[i]) {

now = 0;

dfs(i, 1);

if (now < 0) now = -now;

// cout << now << endl;

sum += now;

for (j = sum; j >= now; j--)

h[j] = h[j] || h[j-now];

}

l = sum/2; r = sum-l;

while (!h[l]) l--, r++;

printf("%d/n", r-l);

}

return 0;

}

模型五:矩陣取數類

題目一:Central Europe 2005

/************************************************************

** Description: 給定二個矩陣,分别有2中礦藏,

第一種礦藏隻能收集到第0列,第二種礦藏隻能收集到第0行

一條直線上隻能有一種運輸方式(從下到上或從右到左)

求獲得的最大礦藏

** Analysis: f[i][j] 表示收集到第i行,第j列的位置時獲得的最大礦藏

f[i][j] = max(f[i-1][j]+row[i][j], f[i][j-1]+col[i][j])

*************************************************************/

#include <iostream>

using namespace std;

const int maxn = 501;

int g[maxn][maxn], h[maxn][maxn], f[maxn][maxn];

int n, m, w;

int max(int a, int b) {

if (a > b) return a; else return b;

}

int main() {

freopen("h.in", "r", stdin);

for (scanf("%d%d", &n, &m); n+m>0; scanf("%d%d", &n, &m)) {

for (int i = 1; i <= n; i++)

for (int j = 1; j <= n; j++) {

scanf("%d", &w);

g[i][j] = g[i][j-1]+w;

}//mine1

for (int i = 1; i <= n; i++)

for (int j = 1; j <= n; j++) {

scanf("%d", &w);

h[j][i] = h[j][i-1]+w;

}//mine2

for (int i = 1; i <= n; i++)

for (int j = 1; j <= m; j++)

f[i][j] = max(f[i-1][j]+g[i][j], f[i][j-1]+h[j][i]);

printf("%d/n", f[n][m]);

}

return 0;

}

模型六:LCS類

題目一:FZU4月月賽G題

/*************************************************

** Descrition: Given two strings

Task One; Get the Largest Common String

Task Two: Get the minimal steps to let the string be the same

you can insert or delete or change a char

** Algorithm: DP

** Analysis: Denote f[i][j] be the maximal LCS of the first i chars in string1 and first j chars in string2

f[i][j] = max(f[i-1][j], f[i][j-1]) s1[i] != S2[J]

f[i-1][j-1]+1 s1[i] == s2[j]

Denote g[i][j] be the minimal steps of the first i chars in string1 and first j chars in string2

g[i][j] = min(g[i][j-1], g[i-1][j], g[i-1][j-1])+1, s1[i] != s2[j]

g[i-1][j-1] s1[i] == s2[j]

**************************************************/

#include <stdio.h>

#include <string.h>

#define MAXL 1010

int cases, i, j;

char s1[MAXL], s2[MAXL];

int f[MAXL][MAXL], g[MAXL][MAXL];

int main() {

freopen("g.in","r", stdin);

scanf("%d/n", &cases);

for (int ci = 1; ci <= cases; ci++) {

gets(s1); gets(s2);

int n1 = strlen(s1);

int n2 = strlen(s2);

f[0][0] = f[1][0] = f[0][1] = 0;

g[0][0] = 0;

g[1][0] = g[1][0] = 1;

for (i = 0; i < n1; i++)

for (j = 0; j < n2; j++)

if (s1[i] == s2[j]) {

f[i+1][j+1] = f[i][j]+1;

g[i+1][j+1] = g[i][j];

} else {

if (f[i+1][j] > f[i][j+1]) {

f[i+1][j+1] = f[i+1][j];

} else {

f[i+1][j+1] = f[i][j+1];

}

if (g[i+1][j] > g[i][j+1]) {

g[i+1][j+1] = g[i][j+1]+1;

} else {

g[i+1][j+1] = g[i+1][j]+1;

}

if (g[i][j]+1 < g[i+1][j+1]) g[i+1][j+1] = g[i][j]+1;

}

printf("Case %d: LCS=%d EditStep=%d/n", ci, f[n1][n2], g[n1][n2]);

}

}

繼續閱讀