這里有一道非常典型的題目:
鏈接戳這里☞:
P1064金明的預算方案
下面是源代碼:
#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
using namespace std;
struct sd{
int weight,value;
bool prime;
}thing;
int M[320005];
int num=0;
bool judger[320005];
vector<sd> subject[80];//subject是指剛剛進入分組時的物品
vector<sd> group[80];//進行后續分組背包時的所用的數組
int m,n;
void init();
int main()
{
init();
int limit=0;
for(int i=1;i<=num;++i)
{
for(int k=limit;k>=0;--k)
{
for(int j=group[i].size()-1;j>=0;--j)
{
if((k==0||M[k]!=0)&&k+group[i][j].weight<=m)
{
if(!judger[k]&&M[k+group[i][j].weight]<M[k]+group[i][j].value)
{
M[k+group[i][j].weight]=M[k]+group[i][j].value;
judger[k+group[i][j].weight]=true;
}
}
if(k+group[i][j].weight>limit)
limit=k+group[i][j].weight;
}
}
memset(judger,false,sizeof(judger));
}
long long ans=0;
for(int i=0;i<=m;++i)
if(ans<M[i])
ans=M[i];
cout<<ans;
return 0;
}
void init()
{
int a,b;
int c;
scanf("%d%d",&m,&n);
for(int i=1;i<=n;++i)
{
scanf("%d%d%d",&a,&b,&c);
thing.value=a*b;
thing.weight=a;
if(c==0)
{
thing.prime=true;
subject[i].push_back(thing);
}
else
{
thing.prime=false;
subject[c].push_back(thing);
}
}
for(int i=1;i<=n;++i)
{
if(subject[i].size()==0)
continue;
else
{
num++;
if(subject[i].size()==1)
{
group[num].push_back(subject[i][0]);
}
else if(subject[i].size()==2)
{
int king;
if(subject[i][0].prime) king=0; else king=1;
group[num].push_back(subject[i][king]);
sd change;
change.weight=subject[i][king].weight+subject[i][1-king].weight;
change.value=subject[i][king].value+subject[i][1-king].value;
group[num].push_back(change);
}
else if(subject[i].size()==3)
{
int king;
if(subject[i][0].prime) king=0; else if(subject[i][1].prime) king=1; else king=2;
group[num].push_back(subject[i][king]);
sd swp;
swp=subject[i][king];
subject[i][king]=subject[i][0];
subject[i][0]=swp;
sd change;
change.weight=subject[i][0].weight+subject[i][1].weight;
change.value=subject[i][0].value+subject[i][1].value;
group[num].push_back(change);
change.weight=subject[i][0].weight+subject[i][2].weight;
change.value=subject[i][0].value+subject[i][2].value;
group[num].push_back(change);
change.weight=subject[i][0].weight+subject[i][1].weight+subject[i][2].weight;
change.value=subject[i][0].value+subject[i][1].value+subject[i][2].value;
group[num].push_back(change);
}
}
}
}
其實說實話,有依賴的背包問題和分組背包問題沒有什么太大的區別,最主要的區別就是我們在進行分組背包前要先進行一次01背包(但是在代碼里我並沒有這樣實現,因為我嫌有一點麻煩,所以我用的強壓的方式,把每一種方案枚舉出來)。后面就和分組背包的思路是一樣的了!
但是這里有一個分組背包以前沒有講到的東西······
下面重點來了!!!
Question:為什么分組背包的limit循環和后面的一個循環組內背包的循環不能反着寫啊?
這個問題非常的important啊!我和我的同學談論了一會兒,終於得出了答案!
為了理解的方便,這里我們采用畫圖的方式進行表達!
1、(錯誤情況)先循環物品,再循環背包
會出現的bug是,我們同組先扔進去的物品會對后面扔進去的物品產生影響,怎么說呢?見下圖:
A:12-------扔入v=7------→B:18
()()()()()()()()()()()()()()
|________________________↑
↑
judger[B]=false
我們可以發現如果在A扔入7那么B點就為19>18,所以此時B點按常理就會自動更新然后把judger變為true,從而對他進行維護。於是此時就為下圖:
A:12-------扔入v=7------→B:18
↓judger[B]=true;
()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
|____________________________↑
本來是可以在這里扔入物品2
到達C點的,可是~~~
judger[B]=true
於是我們成功的發現就這樣,我們就少考慮了一種情況(注意:此情況應該是加上原來的B點位置的值,即judger一來沒有被標記為true的時候),於是在這種少考慮的情況的情況下,很容易就WA掉了幾個點。
2、正確情況為什么就是正確的呢?
因為第二種情況可以有效的避免出現少考慮情況的問題,在我們對一個背包中的點扔同一組的東西時,我們可以保證帶有true標記的judger一定是在這個點后面的,所以不會對前面的點造成影響(相當於為了公平起見,我們這些點都是同時的扔入背包中同一個位置,如果先讓一個點把背包扔一遍,實際上是很不公平的!)
下面放一些圖方便理解:
錯誤的扔法:
step1:
A:12-------扔入v=7------→B:18
()()()()()()()()()()()()()()
|________________________↑
↑
judger[B]=false
step2:
A:12-------扔入v=7------→B:18
↓judger[B]=true;
()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
|————————————|—————————————|____________________________↑
△ 本來是可以在這里扔入物品2
到達C點的,可是~~~
judger[B]=true
相信大家在仔細看了這兩幅圖后,都應該明白了,有△的地方就存在被同一組前一個物品修改的可能!!!因為被修改了原來的值,所以judger變為true然后就少考慮情況了!!!而如果是正確的話(即for循環沒有寫反)那么△的地方就不會存在一來就被某個物品修改的情況,所以不會WA!!!
相信大家認真閱讀后,應該都可以完全理解!
謝謝采納!!!