1. 程式人生 > 其它 >【學習筆記】——揹包 之01揹包

【學習筆記】——揹包 之01揹包

01揹包:
01揹包之所以叫做01揹包,是因為對於每件物品,都只有兩種情況:要或不要

例題:01揹包

分析:每種物品只有兩種情況:要或不要,典型的01揹包
狀態:f[i][v]:前 i 件物品放在 v 空間內產生的最大價值
推導方程:

當第 i 件物品放時:f[i][j]=f[i-1][j-w[i]]+c[i] (即:前 i-1 件物品放在 (j-第 i 件物品所佔空間)時的最大價值 + 第 i 件物品的價值)
不放時:f[i]=f[i-1][j](即:前 i-1 件物品放在 j 空間中的最大值)
又因為要取最大價值,所以:f[i][j]=max(f[i-1][j-w[i]]+c[i],f[i-1][j])

#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[1000][1000];
int w[100100],c[100100];
int main()
{
	cin>>m>>n;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>c[i];
	for(int i=1;i<=n;i++)
		for(int j=n;j>=1;j--)
			f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
	cout<<f[n][m];
	return 0;
}

但這樣的時間、空間複雜度均為O(n*v),其中時間複雜度已無法優化,但空間還可以優化

優化方法一:滾動陣列

如果仔細看一下方程,不難發現,對於當前的f[i][j],其實只用到了第 i 行和第 i-1 行
所以,我們可以將f[n][m]定義為f[2][m](n,m均為長量),再定義兩個變數:pre、cur( pre 表示該行的上一行,cur 表示該行)。
但我們怎麼更改 pre 與 cur 的值呢?
非常簡單:swap(cur,pre) 即可;

#include<bits/stdc++.h>
using namespace std;
int n,m,cur=1,pre;
int w[100100],c[100100],f[1000][1000];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		cin>>w[i]>>c[i];
	for(int i=1;i<=m;i++)
	{
        swap(cur,pre);
		for(int j=n;j>=1;j--)
		{
			if(j>=w[i])
				f[cur][j]=max(f[pre][j],f[pre][j-w[i]]+c[i]);
			else 
				f[cur][j]=f[pre][j];
		}	
	}	
	cout<<f[cur][n];
	return 0;
}
優化方法二:

優化原理同上,不過比上一個更狠:直接壓成一維

方程:f[j]=max(f[j],f[j-w[i]]+c[i])

解釋:

在更新f[j]之前,f[j]相當於之前的f[i-1] [j](可以理解為:a=a+3;) 而更新後,f[j]就成了f[i] [j]

所以,必須從後往前更新,否則,方程就成了f[i][j]=max(f[i][j-w[i]]+c[i],f[i-1][v])

程式碼:

#include<bits/stdc++.h>
using namesapce std;
int n,m;
int w[100100],c[100100];
int main()
{
    cin>>m>>n;
    for(int i=1;i<=n;i++)
        	cin>>w[i]>>c[i];
    for(int i=1;i<=n;i++)
        	for(int j=m;j>=1;j--)
                	if(j>=w[i])
                        	f[j]=max(f[j],f[j-w[i]]+c[i]);
    				else 
                        	f[j]=f[j-1];
    cout<<f[n][m];
    return 0;
}