天天看點

【日常學習】【背包DP(完全背包)】洛谷1616 瘋狂的采藥題解 洛谷1616 瘋狂的采藥

這是一道典型的完全背包題目

先上題目···于是又要迎來洛谷那令人不知道說什麼的霸氣摘要···

洛谷1616 瘋狂的采藥

本題位址: http://www.luogu.org/problem/show?pid=1616

題目背景

此題為NOIP2005普及組第三題的瘋狂版。 此題為紀念LiYuxiang而生。

題目描述

LiYuxiang是個天資聰穎的孩子,他的夢想是成為世界上最偉大的醫師。為此,他想拜附近最有威望的醫師為師。醫師為了判斷他的資質,給他出了一個難題。醫師把他帶到一個到處都是草藥的山洞裡對他說:“孩子,這個山洞裡有一些不同種類的草藥,采每一種都需要一些時間,每一種也有它自身的價值。我會給你一段時間,在這段時間裡,你可以采到一些草藥。如果你是一個聰明的孩子,你應該可以讓采到的草藥的總價值最大。” 

如果你是LiYuxiang,你能完成這個任務嗎?

此題和原題的不同點:

1.每種采藥可以無限制地瘋狂采摘。

2.藥的種類眼花缭亂,采藥時間好長好長啊!師傅等得菊花都謝了!

輸入輸出格式

輸入格式:

輸入第一行有兩個整數T(1 <= T <= 100000)和M(1 <= M <= 10000),用一個空格隔開,T代表總共能夠用來采藥的時間,M代表山洞裡的草藥的數目。接下來的M行每行包括兩個在1到10000之間(包括1和10000)的整數,分别表示采摘某種草藥的時間和這種草藥的價值。

輸出格式:

輸出一行,這一行隻包含一個整數,表示在規定的時間内,可以采到的草藥的最大總價值。

輸入輸出樣例

輸入樣例#1:

70 3
71 100
69 1
1 2
      

輸出樣例#1:

140
      

說明

對于30%的資料,M <= 1000;

對于全部的資料,M <= 10000。

完全背包問題(物體可以選無限件)解決方案:

for (int i=1;i<=m;i++)
	{
		for (int j=v[i];j<=t;j++)
		{
			f[j]=max(f[j],f[j-v[i]]+c[i]);
		}
	}
           

再看01背包如何解決:

for (int i=1;i<=n;i++)
    {
        for (int j=v;j>=a[i];j--)
        {
            f[j]=max(f[j],f[j-a[i]]+a[i]); 
        }
    } 
           

我們驚奇地發現,兩者的差別僅僅在于枚舉體積循環,01背包是倒序,完全背包是正序。這是因為01背包要保證求解f[j]時,f[j-c[i]]是f[i-1][j-c[i]]而不是f[i][j-c[i]]。而完全背包恰恰相反,因為物品可選無數件,我們“選這件”需要考慮這件可能已經選了一些的情況,是以要保證求解f[j]時,f[j-c[i]]指的是f[i][j-c[i]]。

對于完全背包問題的常數優化,可以用“一種簡單有效的優化”,就是把價值低于某物品且體積高于某物品的物品删去。背包九講中提到可以用O(n²)或者O(V+N)的方案實作,但我并不明白具體如何實作,是将該物品對應的兩個數組元素指派為0或某個值,還是怎麼樣?

無論如何,先把代碼放上來吧

//洛谷1616 瘋狂的采藥 背包DP(完全背包)
//copyright by ametake
#include
   
    
#include
    
     
#include
     
      
using namespace std;

const int maxn=10000+10;
const int maxt=100000+10;
int t,m,v[maxn],c[maxn];// v=volumeÌå»ý c=cost=value¼ÛÖµ 
int f[maxt];

int main()
{
	scanf("%d%d",&t,&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&v[i],&c[i]);
	}
	for (int i=1;i<=m;i++)
	{
		for (int j=v[i];j<=t;j++)
		{
			f[j]=max(f[j],f[j-v[i]]+c[i]);
		}
	}
	printf("%d\n",f[t]);
	return 0;
} 
     
    
   
           

——浮天水送無窮樹,帶雨雲埋一半山