1. 程式人生 > >多重部分和問題(動態規劃(DP))

多重部分和問題(動態規劃(DP))

注:文章內容源自《挑戰程式設計競賽》(第二版)

原題

多重部分和問題

有n種不同大小的數字ai,每種各mi個。判斷是否可以從這些數字之中選出若干使它們的和恰好為K。 1<=n<=100 1<=ai,mi<=100000 1<=K<=100000

樣例輸入

n=3 a={3,5,8} m={3,2,2} K=17

樣例輸出

Yes(3*3+8=17)

涉及知識及演算法

定義dp[i+1][j]為前i種數字是否能加和成j 為了用前i種數字加和成j,也就需要能用前i-1種數字加和成j,j-ai,…,j-mi*ai中的某一種。由此我們可以定義如下遞推關係: dp[i+1][j]=(0<=k<=mi且k*ai<=j時存在使dp[i][j-k*ai]為真的k)

程式碼

//數列的長度
int n;
//目標的和數
int K;
//值
int a[MAX_N];
//個數
int m[MAX_N];
bool dp[MAX_N+1][MAX_K+1];

void solve()
{
    dp[0][0]=true;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=K;j++)
        {
            for(int k=0;k<=m[i]&&k*a[i]<=j;k++)
            {
                dp[i+1][j]=dp[i][j-k*a[i]];
            }
        }
    }
    if(dp[n][K])
    {
        printf("Yes\n");
    }
    else
    {
        printf("No\n");
    }
}
這個演算法並不夠好,複雜度為O(K∑imi)。一般用DP求取bool結果會有不少浪費,同樣的複雜度往往能獲得更多資訊。在這個問題中,我們不光求出能否得到目標的和數,同時把得到時ai這個數還剩下多少個可以使用 計算出來,這樣就可以減少複雜度。
可以定義dp[i+1][j]為用前i種數加和得到j時第i種數最多能剩餘多少個(不能加和得到i的情況下為-1) 這樣如果前i-1個數加和能得到j的話,第i個數就可以留下mi個。此外,前i種數加和出j-ai時第i種數還剩下k(k>0)的話,用這i種數加和j時第i種數就能剩下k-1個。由此可得: dp[i+1][j]={ mi(dp[i][j]>=0) { -1 (j<ai或者dp[i+1][j-ai]<=0) { dp[i+1][j-ai]-1 (其他) 這樣只要看最終是否滿足dp[n][K]>=0就可以知道答案了。 這個遞推式可以在O(nK)時間內計算出結果。再將陣列重複利用,則可以得到:
int dp[MAX_K+1];
void solve()
{
    memset(dp,-1,sizeof(dp));
    dp[0]=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=K;j++)
        {
            if(dp[j]>=0)
            {
                dp[j]=m[i];
            }
            else if(j<a[i]||dp[j-a[i]]<=0)
            {
                dp[j]=-1;
            }
            else
            {
                dp[j]=dp[j-a[i]]-1;
            }
        }
    }
    if(dp[K]>=0)
    {
        printf("Yes\n");
    }
    else 
    {
        printf("No\n");
    }
}


相關推薦

多重部分問題動態規劃DP

注:文章內容源自《挑戰程式設計競賽》(第二版) 原題 多重部分和問題 有n種不同大小的數字ai,每種各mi個。判斷是否可以從這些數字之中選出若干使它們的和恰好為K。 1<=n<=100 1<=ai,mi<=100000 1<=K<=100

【BZOJ1415】【NOI2005】聰聰可可動態規劃,數學期望

數學期望 class ios char for problem lin vector noi 【BZOJ1415】【NOI2005】聰聰和可可(動態規劃,數學期望) 題面 BZOJ 題解 先預處理出當可可在某個點,聰聰在某個點時 聰聰會往哪裏走 然後記憶化搜索一下就好了 #

[dp] Codeforces 429B B. Working out動態規劃DP

作者:Accagain 原題 B. Working out time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard output

【BZOJ4872】分手是祝願動態規劃,數學期望

esp math map ostream pac mes ++i rac define 【BZOJ4872】分手是祝願(動態規劃,數學期望) 題面 BZOJ 題解 對於一個狀態,如何求解當前的最短步數? 從大到小枚舉,每次把最大的沒有關掉的燈關掉 暴力枚舉因數關就好 假設我

【題解】 P1879 玉米田Corn Fields 動態規劃,狀態壓縮

bad sin 是否 editor infer nbsp 一行 als clas 題目描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1

ALGO-13 攔截導彈動態規劃貪心

gpo namespace cst 雷達 高度 lan 一個 int post 問題描述   某國為了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到

【BZOJ5299】【CQOI2018】解鎖屏幕動態規劃,狀態壓縮

++ src 規劃 希望 getch cstring online androi 形狀 【BZOJ5299】【CQOI2018】解鎖屏幕(動態規劃,狀態壓縮) 題面 BZOJ 洛谷 Description 使用過Android手機的同學一定對手勢解鎖屏幕不陌生。Androi

【BZOJ4654】【NOI2016】國王飲水記動態規劃,斜率優化

code 奇怪 while lib show ima double 優化 .com 【BZOJ4654】【NOI2016】國王飲水記(動態規劃,斜率優化) 題面 BZOJ 洛谷 題解 首先肯定是找性質。 明確一點,比\(h_1\)小的沒有任何意義。 所以我們按照\(h\)排

【BZOJ3203】保護出題人動態規劃,斜率優化

現在 bzoj3203 d+ while 我們 register 攻擊 nod http 【BZOJ3203】保護出題人(動態規劃,斜率優化) 題面 BZOJ 洛谷 題解 在最優情況下,肯定是存在某只僵屍在到達重點的那一瞬間將其打死 我們現在知道了每只僵屍到達終點的時間,因

【BZOJ1226】學校食堂動態規劃,狀態壓縮

食堂 有關 轉移 mem sizeof fin 狀壓 set lin 【BZOJ1226】學校食堂(動態規劃,狀態壓縮) 題面 BZOJ 洛谷 題解 發現\(b\)很小,意味著當前這個人最壞情況下也只有後面的一小部分人在他前面拿到飯。 所以整個結果的大致順序是不會變化的。

【BZOJ4455】小星星動態規劃,容斥

之間 lld algorithm std 還需要 tchar 一次 lin 還需 【BZOJ4455】小星星(動態規劃,容斥) 題面 BZOJ 洛谷 Uoj 題解 題意說簡單點就是給定一張\(n\)個點的圖和一棵\(n\)個點的樹,現在要讓圖和樹之間的點一一對應,並且如果樹

BZOJ2442 Usaco2011 Open修剪草坪動態規劃+單調隊列

getc math turn size 單調隊列 規劃 lin ret ios   顯然可以dp。顯然可以單調隊列優化一下。 #include<iostream> #include<cstdio> #include<cmath> #i

【BZOJ1294】[SCOI2009]圍豆豆動態規劃,狀壓

bool pre max += 網格 中心 是否 ret algo 【BZOJ1294】[SCOI2009]圍豆豆(動態規劃,狀壓) 題面 BZOJ 洛谷 題解 首先考慮如何判斷一個點是否在一個多邊形內(不一定是凸的),我們從這個點開始,朝著一個方向畫一條射線,看看它和這個

BZOJ4011 HNOI2015落憶楓音動態規劃+拓撲排序

  DAG中每個點選一條入邊就可以構成一棵有向樹,所以如果沒有環答案就是∏degreei。   考慮去掉含環的答案。可以看做把環縮點,剩下的點仍然可以任意選入邊。於是去除的方案數即為∏degreei/∏degreek,k為環上點。   環相當於考慮新加入邊的終點到起點的所有路徑。設f[i]為i為起點的所有

LeetCode打家劫舍問題動態規劃演算法的理解

前幾天在LeetCode的時候碰到幾道打家劫舍問題,覺得挺有意思,在這裡跟大家一起學習一下。首先我們來看第一題: 相信有的朋友拿到這道題想法是和我一樣的,那就是暴力解法,我用兩層for迴圈來遍歷每一個房間,後來發現這樣其實太麻煩了,並且還無法考慮到一些特定的情況,所以提交了很多次都

BZOJ4887 Tjoi2017可樂動態規劃+矩陣快速冪

  設f[i][j]為第i天到達j號城市的方案數,轉移顯然,答案即為每天在每個點的方案數之和。矩乘一發即可。 #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #

最長公共子串LCS問題動態規劃及備忘錄方法

動態規劃與備忘錄的LCS實現 動態規劃從下到上積累能量,中間值全部記錄以方便後期計算時呼叫,但有些時候很多記錄的值用不到,這個時候備忘錄方法則更好,可以減少記錄的值,其特點是自上到下,記錄少部分值。以LCS最長公共子串問題威力,分別給出兩種實現。 動態規劃法: pa

面試題14:剪繩子動態規劃,貪心演算法

一、題目: 一根長度為n的繩子,剪成m段,m,n都大於1,且都為整數,每段長度記為k[0],k[1]…,k[m].求k[0]*k[1]…*k[m]可能的最大乘積 1.1解法: 兩種不同的方法解決這個問題,先用常規的需要O(n²)時間和O(n)空間的動態規劃,接著用只需要O(1)的

BZOJ5298 CQOI2018交錯序列動態規劃+矩陣快速冪

  顯然答案為Σkb·(n-k)a·C(n-k+1,k)。並且可以發現ΣC(n-k,k)=fibn。但這實際上沒有任何卵用。   純組合看起來不太行得通,換個思路,考慮一個顯然的dp,即設f[i][j][0/1]為前i為選了j個1其中第i位是0/1的方案數。這樣當然能求出答案,複雜度O(n2)。   注意

最少硬幣問題動態規劃遞推式

最少硬幣問題 時間限制(普通/Java) : 1000 MS/ 3000 MS          執行記憶體限制 : 65536 KByte總提交 : 247            測試