昨晚contest的Problem E,freezhan學姐用揹包寫AC了,但是優化成一維後連樣例都過不了,然後我就研究了一下。
這是優化前的代碼:
F[0][0]=1;
for(int i=1;i<=N;++i)
{
for(int j=M;j>=A[i];--j)
{
for(int k=A[i];k<=B[i]&&k<=j;++k)
{
F[i][j]+=F[i-1][j-k];
}
}
}
我想肯定有不少人會像我和freezhan學姐一樣把這份代碼優化爲:
F[0]=1;
for(int i=1;i<=N;++i)
{
for(int j=M;j>=A[i];--j)
{
for(int k=A[i];k<=B[i]&&k<=j;++k)
{
F[j]+=F[j-k];
}
}
}
對拍,測試,調整,完事發現這個問題很久之前就注意到了卻沒重視,看來以後哪個小問題都不能放過!
優化出錯的根本原因是滾動數組的不完全覆蓋!
大多數的揹包問題最高維(一般就是物品編號)的每一次循環求解的第二維區間(一般就是[0,揹包容量])都是相同的。
如果用滾動數組優化成一維,每一次循環之後當前數據會完全覆蓋之前的數據,下一次循環時調用的數據就全部是更新了
的數據。
但是本題的最高維(i)的每一次循環求解的第二維區間([A[i],M])卻不盡相同!
如果用滾動數組優化成一維,每一次循環之後當前數據可能僅會部分覆蓋之前的數據!這就造成了數據殘留,下一次
循環時調用的數據就可能是殘留數據!
正確的做法應該是用滾動數組優化成二維:
int roll=0;
memset(F[roll],0,sizeof(F[roll]));
F[roll][0]=1;
for(int i=1;i<=N;++i)
{
roll=!roll;
memset(F[roll],0,sizeof(F[roll]));
for(int j=M;j>=A[i];--j)
{
for(int k=A[i];k<=B[i]&&k<=j;++k)
{
F[roll][j]+=F[!roll][j-k];
}
}
}