01揹包(降維 + 常數級優化)

題目:

    共n個物體,第i個重量爲w[i],價值v[i],揹包最多能背不超過W的物體,求最大的價值

分析:

    每個物體只有一個,在容量允許時(W>w[i]),則對於每個物體只有取、不取兩種選擇

    狀態:dp[i][j]:前i個物體,在容量爲j的時候,最大的價值

    狀態轉移:

        dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);

二維核心:

for(i = 1; i<=n; i++)
{
    for(j = 0; j<=W; j++)
    {
        if(j<w[i])
            dp[i][j] = dp[i-1][j];
        else
            dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
    }
}

二維代碼:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>

using namespace std;

int w[100], v[100];
int dp[100][100];

int main()
{
	freopen("a.txt", "r", stdin);
	int n, W, i, j;
	while(~scanf("%d%d", &W, &n))
	{
		memset(dp, 0, sizeof(dp));
		for(i = 1; i<=n; i++)
		{
			scanf("%d%d", &w[i], &v[i]);
		}
		for(i = 1; i<=n; i++)
		{
			for(j = 0; j<=W; j++)
			{
				if(j<w[i])
					dp[i][j] = dp[i-1][j];
				else
					dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
			}
		}
		printf("%d\n", dp[n][W]);
	}
	return 0;
}

降維:

    減行,第i個物體的更新,只依賴於第i-1個的物體的結果

    所以可以用滾動數組,每次只存i和i-1時候的值 (可得:dp[n][W] → dp[2][W] )

    刪行,第i個物體在容積爲j狀態的更新,只依賴i-1物體容量裏j-w[i]的狀態的結果

    所以,從後面開始向前更新,則求j位置時候,j-w[i]的值依舊爲i-1時候的值(可得:dp[n][W] → dp[W] )

一維核心:

for(i = 1; i<=n; i++)
{
    for(j = W; j>=w[i]; j--) //從後向前,此時dp[j-w[i]]相當於dp[i-1][j-w[i]]
    {
        dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
    }
}

一維代碼:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>

using namespace std;

int w[100], v[100];
int dp[100];

int main()
{
	freopen("a.txt", "r", stdin);
	int n, W, i, j;
	while(~scanf("%d%d", &W, &n))
	{
		memset(dp, 0, sizeof(dp));
		for(i = 1; i<=n; i++)
		{
			scanf("%d%d", &w[i], &v[i]);
		}
		for(i = 1; i<=n; i++)
		{
			for(j = W; j>=w[i]; j--)
			{
				dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
			}
		}
		printf("%d\n", dp[W]);
	}
	return 0;
}

初始化:

    1、memset(dp, 0, sizeof(dp))

        求不超過容積的W的最大價值

        容積有剩餘的狀態依舊有值,爲前一個恰好裝滿最優解的值

    2、memset(dp, -0x3f, sizeof(dp)); //負無窮、不可達點(當前值約爲:-1e+10)

        求恰好裝滿容積的最大價值(可能無解)

        當且僅當恰好裝滿的狀態有值,其他存在空白容積的狀態無法到達

常數級優化:

    一維中的內循環下限,由j>=w[i] → j>=max{w[i], W-(∑(i,n)w[i])}

    1、下限爲j>=w[i]時候

        在所有剩餘容積大於等於w[i]時候,選擇取、不取第i物品

    2、下限爲j>=max{w[i], W-(∑(i,n)w[i])}時候

        只更新在i+1時候需要用到的狀態,並不把所以可能狀態求出

常數級優化代碼:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>

using namespace std;

int w[100], v[100];
int dp[100];

int main()
{
	freopen("a.txt", "r", stdin);
	int n, W, i, j;
	while(~scanf("%d%d", &W, &n))
	{
		memset(dp, 0, sizeof(dp));
		for(i = 1; i<=n; i++)
		{
			scanf("%d%d", &w[i], &v[i]);
		}
		int lower, sum = 0;
		for(i = 1; i<=n; i++)
		{
			if(i!=1) sum += w[i-1];
			lower = max(sum, w[i]);
			for(j = W; j>=lower; j--)
			{
				dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
			}
		}
		printf("%d\n", dp[W]);
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章