模型一:石子歸併類
題目一:Northeastern Europe 2001, Far-Eastern Subregion
/*******************************************************<br />** Description: 給定N張卡片,每次只能從中間取卡片,<br />取走一張卡片的價值為這張卡片的價值和相鄰的卡片價值的乘積,<br />求最後剩下2張卡片時,能獲得的最小价值是多少?<br />** Analysis:f[i][j] 表示 從 第i張 到 第j張,能或得的最小价值<br />階段:取掉的連續區間的長度<br />決策:f[i][j] = min(f[i+1][j]+w[i-1]*w[i]*w[j+1],<br /> f[i][j-1]+w[j+1]*w[j]*w[i-1],<br /> f[i][k-1]+f[k+1][j]+w[k]*w[i-1]*w[j+1]), i < k < j<br />k表示在這個區間中最後取走的牌的編號<br />** Time:O(N^3)<br />*******************************************************/<br />#include <iostream><br />using namespace std;<br />const int maxn = 101;<br />int a[maxn];<br />long long f[maxn][maxn];<br />int n;<br />long long min(long long a, long long b) {<br />if (a < b) return a; else return b;<br />}<br />int main() {<br />scanf("%d", &n);<br />for (int i = 1; i <= n; i++) scanf("%d", &a[i]);<br />for (int i = 2; i < n; i++) f[i][i] = a[i-1]*a[i]*a[i+1];<br />for (int k = 2; k <= n-2; k++)<br />for (int i = 2; i <= n-k; i++) {<br /> int j = i+k-1;<br /> 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]);<br /> for (int p = i+1; p < j; p++)<br />f[i][j] = min(f[i][j], f[i][p-1]+f[p+1][j]+a[p]*a[i-1]*a[j+1]);<br />}<br />printf("%I64d/n", f[2][n-1]);<br />return 0;<br />}<br />
模型二:同餘類
題目一:Northeastern Europe 1999
/*****************************************************************<br />** Description:給定N個數,求有這N個數加減能否得到某一個數P的倍數?<br />** Analysis:f[i][j] 表示用前i個數能否得到模P餘J<br />f[i][j] = f[i-1][(j-w[i])modP] || f[i-1][(j+w[i])modP]<br />但要注意 % 操作對於負數的情況 -1 % 5 = -1<br />** Time:O(P*N)<br />*****************************************************************/<br />#include <iostream><br />using namespace std;<br />int n, p, m, state;<br />int h[2][101];<br />int f(int k) {<br />k = k % p;<br />k = (k+p)%p;<br />return k;<br />}<br />int main() {<br />while (scanf("%d%d", &n, &p) == 2) {</p><p>memset(h,0,sizeof(h));<br />state = 0; h[0][0] = 1;<br />for (int i = 0; i < n; i++) {<br />scanf("%d", &m);<br />state = 1 - state;<br />for (int j = 0; j < p; j++) h[state][j] = 0;<br />for (int j = 0; j < p; j++)<br />if (h[1-state][j]) h[state][f(j+m)] = h[state][f(j-m)] = 1;<br />}<br />if (h[state][0]) printf("Divisible/n"); else printf("Not divisible/n");</p><p>}<br />return 0;<br />}<br />
模型三:最長不下降子序類
題目一: Waterloo local 2000.09.23
/*****************************************************************<br />** Description: 按字典序給出N個單詞,對一個單詞可以進行修改一個字母,添加一個字母,刪除一個字母的操作<br /> 求出做多選出多少個單詞,使得相鄰兩個滿足進行一次操作<br />** Analysis: 每次構造出由但前單詞可以構造出的所有單詞,然後去判斷前面是否出現過,找出一個最大值<br />演算法1:trie 演算法2:二分<br />*****************************************************************/<br />#include <iostream><br />#include <string.h><br />using namespace std;<br />const int maxn = 25010;<br />int n, len, k;<br />int f[maxn];<br />char w[maxn][20], s[20];<br />int check() {<br />int l = 1, r = n, mid;<br />while (l+1<r) {<br />mid = (l+r)/2;<br />if (strcmp(s, w[mid]) >= 0) l = mid; else r = mid;<br />}<br />if (strcmp(w[l], s) == 0) return l; else return 0;<br />}<br />int main() {<br />freopen("e.in", "r",stdin);<br />freopen("e.out", "w", stdout);<br />n = 0; f[0] = 0;<br />while (scanf("%s", w[++n]) == 1) {<br />len = strlen(w[n]); f[n] = 1;<br />//cout << w[n] << endl;<br />if (n == 1) continue;<br />//changing<br />for (int i = 0; i < len; i++) s[i] = w[n][i]; s[len] = '/0';<br />for (int i = 0; i < len; i++) {<br />for (char j = 'a'; j < w[n][i]; j++) {<br />s[i] = j;<br />//cout << s << endl;<br />k = check();<br />if (f[k]+1>f[n]) f[n] = f[k]+1;<br />}<br />s[i] = w[n][i];<br />}<br />//delete<br />s[len-1] = '/0';<br />if (len > 1)<br />for (int i = 0; i < len; i++) {<br />for (int j = 0; j < i; j++) s[j] = w[n][j];<br />for (int j = i+1; j < len; j++) s[j-1] = w[n][j];<br />//cout << s << endl;<br />len--; k = check(); len++;<br />if (f[k]+1>f[n]) f[n] = f[k]+1;<br />}</p><p>//addition<br />s[len+1] = '/0';<br />for (int i = 0; i < len; i++) s[i+1] = w[n][i];<br />for (int i = 0; i < len; i++) {<br />len++;<br />for (char j = 'a'; j <= w[n][i]; j++) {<br />s[i] = j;<br />//cout << s << endl;<br />k = check();<br />if (f[k]+1>f[n]) f[n] = f[k]+1;<br />}<br />len--;<br />s[i] = w[n][i];<br />}<br />}</p><p>int max = 0;<br />for (int i = 1; i <= n; i++)<br />if (f[i] > max) max = f[i];<br />printf("%d/n", max);<br />return 0;<br />}<br />
題目二:Northeastern Europe 1998
/***************************************************<br />** Desrtiption: 有N個賊,每個賊在某一個時刻Ti來到商店,商店有有0--K的門,<br /> 只有當前商店門的編號與賊心中的編號Si附合時才會偷價值為Pi的物品<br /> 在一個單位時間內門的編號可以就+1 0 -1<br /> 求在可以操作門的情況下,最多能偷走物品數?<br />** Analysis: 來的時間有先後,排個序!<br />f[i] 表示以第i人偷時的最優值,那麼<br />f[i] = max(f[j]+p[i]), |s[i]-s[j]|<=|t[i]-t[j]|, 1 <= j < i<br />** Time:O(N^2)<br />/***************************************************<br />#include <iostream><br />using namespace std;<br />const int maxn = 101;<br />int t[maxn], p[maxn], s[maxn], f[maxn];<br />int n, k, tt;<br />void swap(int &a, int &b) {<br />int t = a; a = b; b = t;<br />}<br />int main() {<br />freopen("g.in", "r", stdin);<br />scanf("%d%d%d", &n, &k, &tt);<br />for (int i = 1; i <= n; i++) scanf("%d", &t[i]);<br />for (int i = 1; i <= n; i++) scanf("%d", &p[i]);<br />for (int i = 1; i <= n; i++) scanf("%d", &s[i]);</p><p>//sort<br />for (int i = 1; i < n; i++)<br />for (int j = i+1; j <= n; j++)<br />if (t[j] < t[i]) {<br />swap(t[i], t[j]);<br />swap(p[i], p[j]);<br />swap(s[i], s[j]);<br />}</p><p>t[0] = 0; s[0] = 0; f[0] = 0;<br />for (int i = 1; i <= n; i++) {<br />f[i] = -1 << 30;//necessary<br />for (int j = 0; j < i; j++)<br />if (abs(s[i]-s[j]) <= abs(t[i]-t[j]))<br />if (f[j] + p[i] > f[i])<br />f[i] = f[j] + p[i];<br />}</p><p>int max = 0;<br />for (int i =1; i <= n; i++)<br />if (f[i] > max) max = f[i];<br />printf("%d/n", max);<br />return 0;<br />}<br />
模型四:背包類
NUDT-P2180
/******************************************<br />** Description: 給定N個物品, 給出這N個物品之間的關係,有直接關係的不能放進同一堆<br />求把這N個物品放進兩堆最小差值是多少?<br />** Analysis: 初看好像和背包沒有關係???對於一個連通分量轉化成二分圖後,兩堆的差值是確定的<br />所以可以把這個差值作為一個物品的重量去做,轉化為M個物品分成兩堆的最小差距,就可以用背包完成了<br />********************************************/<br />#include <iostream><br />using namespace std;<br />const int maxn = 100, maxv = 2010;<br />int n, i, j, now, l, r, sum, c[maxn];<br />bool d[maxn][maxn], h[maxv], v[maxn];<br />char map[110];<br />void dfs(int x, int sgn) {<br />now += sgn*c[x];<br />v[x] = 1;<br />for (int i = 0; i < n; i++)<br />if (!v[i] && d[x][i]) dfs(i, -sgn);<br />}<br />int main() {<br />freopen("d.in", "r", stdin);<br />while (scanf("%d", &n) == 1) {<br />for (i = 0; i < n; i++) scanf("%d", &c[i]);<br />for (i = 0; i < n; i++) {<br />scanf("%s", map);<br />for (j = 0; j < i; j++)<br />if (map[j] == '1')<br />d[i][j] = d[j][i] = 1;<br />else<br />d[i][j] = d[j][i] = 0;<br />}<br />memset(v,0,sizeof(v));<br />memset(h,0,sizeof(h));<br />h[0] = 1; sum = 0;<br />for (i = 0; i < n; i++)<br />if (!v[i]) {<br />now = 0;<br />dfs(i, 1);<br />if (now < 0) now = -now;<br />//cout << now << endl;<br />sum += now;<br />for (j = sum; j >= now; j--)<br />h[j] = h[j] || h[j-now];<br />}<br />l = sum/2; r = sum-l;<br />while (!h[l]) l--, r++;<br />printf("%d/n", r-l);<br />}<br />return 0;<br />}<br />
模型五:矩陣取數類
題目一:Central Europe 2005
/************************************************************<br />** Description: 給定二個矩陣,分別有2中礦藏,<br />第一種礦藏只能收集到第0列,第二種礦藏只能收集到第0行<br />一條直線上只能有一種運輸方式(從下到上或從右至左)<br />求獲得的最大礦藏<br />**Analysis: f[i][j] 表示收集到第i行,第j列的位置時獲得的最大礦藏<br />f[i][j] = max(f[i-1][j]+row[i][j], f[i][j-1]+col[i][j])<br />*************************************************************/<br />#include <iostream><br />using namespace std;<br />const int maxn = 501;<br />int g[maxn][maxn], h[maxn][maxn], f[maxn][maxn];<br />int n, m, w;<br />int max(int a, int b) {<br />if (a > b) return a; else return b;<br />}<br />int main() {<br />freopen("h.in", "r", stdin);<br />for (scanf("%d%d", &n, &m); n+m>0; scanf("%d%d", &n, &m)) {<br />for (int i = 1; i <= n; i++)<br />for (int j = 1; j <= n; j++) {<br />scanf("%d", &w);<br />g[i][j] = g[i][j-1]+w;<br />}//mine1<br />for (int i = 1; i <= n; i++)<br />for (int j = 1; j <= n; j++) {<br />scanf("%d", &w);<br />h[j][i] = h[j][i-1]+w;<br />}//mine2<br />for (int i = 1; i <= n; i++)<br />for (int j = 1; j <= m; j++)<br />f[i][j] = max(f[i-1][j]+g[i][j], f[i][j-1]+h[j][i]);<br />printf("%d/n", f[n][m]);<br />}<br />return 0;<br />}<br />
模型六:LCS類
題目一:FZU4月月賽G題
/*************************************************<br />** Descrition:Given two strings<br />Task One; Get the Largest Common String<br />Task Two: Get the minimal steps to let the string be the same<br /> you can insert or delete or change a char<br />** Algorithm:DP<br />** Analysis:Denote f[i][j] be the maximal LCS of the first i chars in string1 and first j chars in string2<br />f[i][j] = max(f[i-1][j], f[i][j-1]) s1[i] != S2[J]<br /> f[i-1][j-1]+1s1[i] == s2[j]<br />Denote g[i][j] be the minimal steps of the first i chars in string1 and first j chars in string2<br />g[i][j] = min(g[i][j-1], g[i-1][j], g[i-1][j-1])+1, s1[i] != s2[j]<br /> g[i-1][j-1]s1[i] == s2[j]<br />**************************************************/<br />#include <stdio.h><br />#include <string.h><br />#define MAXL 1010<br />int cases, i, j;<br />char s1[MAXL], s2[MAXL];<br />int f[MAXL][MAXL], g[MAXL][MAXL];<br />int main() {<br />freopen("g.in","r", stdin);<br />scanf("%d/n", &cases);<br />for (int ci = 1; ci <= cases; ci++) {<br />gets(s1); gets(s2);<br />int n1 = strlen(s1);<br />int n2 = strlen(s2);<br />f[0][0] = f[1][0] = f[0][1] = 0;<br />g[0][0] = 0;<br />g[1][0] = g[1][0] = 1;<br />for (i = 0; i < n1; i++)<br />for (j = 0; j < n2; j++)<br />if (s1[i] == s2[j]) {<br />f[i+1][j+1] = f[i][j]+1;<br />g[i+1][j+1] = g[i][j];<br />} else {<br />if (f[i+1][j] > f[i][j+1]) {<br />f[i+1][j+1] = f[i+1][j];<br />} else {<br />f[i+1][j+1] = f[i][j+1];<br />}<br />if (g[i+1][j] > g[i][j+1]) {<br />g[i+1][j+1] = g[i][j+1]+1;<br />} else {<br />g[i+1][j+1] = g[i+1][j]+1;<br />}<br />if (g[i][j]+1 < g[i+1][j+1]) g[i+1][j+1] = g[i][j]+1;<br />}<br />printf("Case %d: LCS=%d EditStep=%d/n", ci, f[n1][n2], g[n1][n2]);<br />}<br />}<br />