1. 程式人生 > >dp入門 專題記錄 2017-7-26

dp入門 專題記錄 2017-7-26

開始 com 結束時間 opened long cout 方程 log star

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 就 +1
       for(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

dp入門 專題記錄 2017-7-26