dp入门 专题记录 2017-7-26
POJ3176-Cow Bowling
題目大意:現有n行數,以金字塔的形式排列,即第一行一個數字,第二行2個數字,依次類推,現在需要找一條從第一層到第n層的路線,使得該路線上的所有點的權值和最大
思路:根據分析可以得出狀態轉移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]),dp[i][j]表示以第i行第j個位置作為終點的的線路中的最大權值。
#include <iostream> using namespace std; const int N = 360; int s[N][N]; int dp[N][N]; int main () {int n;cin >> n;for(int i=1;i <= n;i++)for(int j=1;j <= i;j++)cin >> s[i][j];for(int i=n;i ;i--){for(int j=1;j <= i;j++)//從下網上依次找到最大的 {dp[i][j] = s[i][j] + max(dp[i+1][j],dp[i+1][j+1]);}}cout<< dp[1][1]<<endl;return 0; } A?
Poj 2229(dp)
題目大意:求把一個整數分解為2的冪的和共有幾種方案
6=1+1+1+1+1+1
6=1+1+1+1+2
6=1+1+2+2
6=1+1+4
6=2+2+2
6=2+4
思路1:
如果i為奇數,肯定有一個1,把f[i-1]的每一種情況加一個1就得到fi,所以f[i]=f[i-1]
如果i為偶數,如果有1,至少有兩個,則f[i-2]的每一種情況加兩個1,就得到i,
如果沒有1,則把分解式中的每一項除2,則得到f[i/2] ?(比如 4 = 2+2 ,4 = 4 ?除2后就變成 2 = 1 + 1 , 2 = 2) ?
所以f[i]=f[i-2]+f[i/2]
#include <iostream> #include <cstdio>using namespace std; const int mod = 1e9 ; const int maxn = 1000000 + 5; typedef long long LL;LL dp[maxn];int main() {int n;cin >> n;dp[0] = 1,dp[1] = 1;for(int i=2;i<=n;i++){if(i & 1)//奇數dp[i] = dp[i-1];else//偶數 {dp[i] = dp[i-2] + dp[i/2],dp[i] %= mod;}}cout<< dp[n] <<endl;return 0; } B_遞推?思路2:
題目分析: d[i][v] ?表示前i物品之和為v的最多數量, ?有狀態轉移方程 d[i][v] ?= sum( d[i-1][v-k*c[i]] | ? 0 < k*c[i] <=v)
? ? ? ? ? ? ? ? ? ? ? 利用完全背包O(vn)優化的思想, 這里思想是相同的,則d[v] += d[v-c[i]]?
#include <iostream> #include <cstdio>using namespace std; const int mod = 1e9; const int maxn = 1e6 + 5; const int INF = 0x3f3f3f3f; typedef long long LL; int s[20]; LL dp[maxn];int main() {int n;scanf("%d",&n);s[0] = 1;for(int i=1;i<20;i++)s[i] = s[i-1]*2; //打表記錄 2的0-20次方dp[0] = 1;for (int i =0;i < 20; i++){if(s[i] > n) break;for(int j=s[i];j <= n ;j++){dp[j] += dp[ j-s[i] ] ;if(dp[j] > mod )dp[j] %= mod;}}printf("%lld",dp[n]);return 0; } B_完全背包 容易T?
poj 2385?Apple Catching
題意:有兩棵蘋果樹,標號分別為1,2。每分鐘有其中的一棵樹會掉下一個蘋果,奶牛一分鐘只能在其中一棵樹下接到蘋果,但她不知道下一分鐘會是那棵樹掉下蘋果,所以她就要在兩棵樹下來回跑。但她只會在樹下跑W次。問你在T分鐘內,奶牛bessie最多能接到多少蘋果。
?
思路:一道簡單的DP。
先給出狀態轉移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+count。這里的dp[i][j]代表在第i分鐘移動j次最多能接到的蘋果數。
在第i分鐘奶牛到某棵樹下有兩種狀態:
1.從另一棵樹走過來(dp[i-1][j-1])
2.本來就呆在這棵樹下(dp[i-1][j])。所以在第i分鐘時能接到的最大蘋果數就是dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+count。
這里count的值可以這樣計算:如果j為偶數說明她移動了j次又回到了第一棵樹下,則count=a[i]==1?1:0;即count=2-a[i]。若j為奇數說明她移動了j次后到了第二棵樹下,則count=a[i]==2?1:0(即count=a[i]-1)。
?
#include <iostream> #include <cstdio>using namespace std; const int mod = 1e9 + 7; const int maxn = 1000 + 5; const int INF = 0x3f3f3f3f; typedef long long LL; int s[maxn]; int dp[maxn][40];//p[i][j]代表第i棵樹 最多j次走 最大能吃到的Apple int solve (int t,int w) {int count;for(int i=0;i <= w;i++) dp[0][i] = 0;//初始化for(int i =1;i <= t;i++)//一共最多w次 {dp[i][0] = dp[i-1][0] + 2-s[i]; //如果一次沒動過 只要s[i] = 1 就 +1for(int j=0;j <= w;j++){if(j % 2) //j是奇數 此時在第2顆樹上count =s[i] -1 ; //如果s[i] =2 說明有1次else //j是偶數 此時在第1顆樹上count = 2-s[i];dp[i][j] = max(dp[i-1][j-1],dp[i-1][j]) + count;}}count = 0;for(int i=1;i <= t;i++){for(int j=0;j <= w ;j++){if(dp[i][j] > count )count = dp[i][j];}}return count; }int main() {int t,w;scanf("%d %d",&t,&w);for(int i=1;i <= t;i++){cin >> s[i];}cout<< solve(t,w)<<endl;return 0; } C?
//其實第三題的 count不需要比較 因 //為遞推的時候dp = max(dp[i-1][j] ,dp[i][j]) 了 就是現在的狀態記錄的已經是最優解 count = 0;for(int i=1;i <= t;i++){for(int j=0;j <= w ;j++){if(dp[i][j] > count )count = dp[i][j];}}return count; //所以這段可以 直接修改為 return dp[t][w]; c題補充??
POJ3616Milking Time
?
題意:
在一個農場里,在長度為N個時間可以擠奶,但只能擠M次,且每擠一次就要休息t分鐘;
接下來給m組數據表示擠奶的時間與奶量求最大擠奶量
思路:
每次 結束時間 += 休息的時間,接著按照 開始時間 排序
接著 狀態轉移方程 dp[i] = max(dp[i] , dp[j] + dp[i].cost) ( j > i && s[j].start >= s[i].end )
#include <iostream> #include <algorithm> using namespace std; struct P{int left,ri,cost;bool operator < (const P & other)const{return (left < other.left||(left == other.left) &&ri < other.ri );} }s[1010];int dp[1010];int main () {int n,m,t;cin >>n>>m>>t;for(int i=1;i<= m;i++){cin >> s[i].left>> s[i].ri >> s[i].cost;s[i].ri += t;}sort(s+1,s+1+m);for(int i=m; i ;i--){dp[i] = s[i].cost;for(int j=i+1 ; j <= m;j++){if(s[j].left >= s[i].ri)dp[i] = max(dp[i], dp[j] + s[i].cost);//狀態轉移方程 }}int count = 0;for(int i=1;i <= m;i++)count = max(count ,dp[i]);cout<< count<<endl;return 0; } D??
poj(3280)Cheapest Palindrome(區間dp)
題意:
給出一個由m中字母組成的長度為n的串,給出m種字母添加和刪除花費的代價,求讓給出的串變成回文串的代價。?
思路:
我們知道求添加最少的字母讓其回文是經典dp問題,轉化成LCS求解。這個是一個很明顯的區間dp
我們定義dp [ i ] [ j ] 為區間 i 到 j 變成回文的最小代價。
那么對于dp [ i ] [ j ]有三種情況
首先:對于一個串如果s[ i ]==s[ j ],那么dp [ i ] [ j ]=dp [ i+1 ] [ j-1 ]
其次:如果區間 [ i+1, j ]已經維護成回文串,那么dp [ i ] [ j ]=dp [ i+1 ] [ j ]+min(add[ i ],del[ i ]);
最后,如果區間?[ i, j-1 ]已經維護成回文串,那么dp [ i ] [ j ]=dp [ i ] [ j-1 ]+min(add[ j ],del[ j ]);
#include <stdio.h> #include <string.h> #include <cmath> #include <iostream> using namespace std;int dp[2005][2005]; int cost[300]; int n,m; char s[2005];int main () {while (cin >> m >> n)//m表示可以增刪的字符的個數 n表示字符串長度 {memset(dp,0,sizeof(dp));cin >> s+1 ;for(int i=0;i < m;i++){char ch; int x ,y;cin >> ch >>x >> y;cost[ ch ] = min(x,y);}//存儲數據for(int i=n; i ;i--){for(int j=i+1;j <= n;j++){if( s[i] == s[j] )dp[i][j] = dp[i+1][j-1];//長度收縮一下elsedp[i][j] = min(dp[i+1][j] + cost[ s[i] ],dp[i][j-1] + cost[ s[j] ]);}}cout<< dp[1][n] <<endl;}return 0; } E?
POJ 1742 Coins
題意:
給出n個coins 的價值 和 數量 求出 可以構造出 小于m的數量
思路:
基本就是挑戰的原題了 ? 用的多重部分和 看到網上說的就是多重背包
加深了一點點兒的想法把
dp[i][j] := 用前i種硬幣湊成j時第i種硬幣最多能剩余多少個(-1表示配不出來)如果dp[i - 1][j] >= 0(前i-1個數可以湊出j,那么第i個數根本用不著)直接為C[i] dp[i][j] = 如果j < A[i]或者dp[i][j - a[i]] <=0 (面額太大或者在配更小的數的時候就用光了)-1其他(將第i個數用掉一個) dp[i][j-a[i]] - 1?
#include <iostream> #include <string.h> #include <algorithm> using namespace std; const int maxn = 100010;int price[105] , num[105]; int dp[maxn]; //dp[i][j]表示在用前i種coins 用的第i種硬幣還剩余的情況 int n,m;int main () {while (cin >> n >> m && n+m){for(int i=1;i <= n;i++ )cin >> price[i];for(int i=1;i <= n;i++)cin >>num[i];memset(dp,-1,sizeof(dp));dp[0] = 0;for(int i=1;i <= n;i++){for(int j=0;j <= m;j++){if(dp[ j ] >= 0)//前面 i-1已經算出來了dp[j] = num[i];else if(dp[ j-price[i] ] <= 0 || j < price[i] )dp[j] = -1;elsedp[j] = dp[j-price[i] ] -1;}}int ans = 0;for(int i=1;i <= m;i++)if( dp[i] >= 0 )ans++;cout<< ans <<endl;}return 0; } F http://www.hankcs.com/program/cpp/poj-1742-coins.html 參考網址?
?背包加深一下理解 ?就是 如果dp優化成一維的 ?如果需要更新自己當前行的值 就 j從0到m ?如果不需要更新自己當前行的值 ?j就從m到0
?
轉載于:https://www.cnblogs.com/Draymonder/p/7220738.html
總結
以上是生活随笔為你收集整理的dp入门 专题记录 2017-7-26的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 风险纳税人是什么意思
- 下一篇: Codeforces Round #42