生活随笔
收集整理的這篇文章主要介紹了
背包问题——第一篇
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一,01背包
最簡單也是最經典的背包問題。
首先我們知道背包問題是一種d問題,最重要的就是要去找到他的狀態(tài)轉移方程。而在01背包中轉移方程就比較簡單了,這里用一個二維數組進行標表示。
ans[i][j]=max(ans[i-1][j],ans[i-1][j-v[i]+w[i]);
在這里i表示的是第幾件物品,j表示的是背包當前課裝下的最大體積,
0 1 就是有兩種選擇:
不裝第i件物品,此時的狀態(tài)就是,裝上一件物品的相同體積的狀態(tài)。
裝第i件物品,此時還要考慮當前的背包容量能不能裝得下,如果可以,就裝下,此時的價值變成了,ans[i-1][j-v[i]]+w[i],
因為我們要的是在背包中盡可能的裝入跟多的價值,所以這里就要取一個max值
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 1005
int ans
[maxn
][maxn
];
int v
[maxn
],w
[maxn
];
using namespace std
;
int main() {int n
,m
;while(cin
>>n
>>m
) {memset(ans
,0,sizeof(ans
));for(int i
=1;i
<=n
;i
++)cin
>>v
[i
]>>w
[i
];ans
[0][0]=0;for(int i
=1;i
<=n
;i
++) {for(int j
=0;j
<=m
;j
++) {ans
[i
][j
]=ans
[i
-1][j
];if(j
>=v
[i
])ans
[i
][j
]=max(ans
[i
][j
],ans
[i
-1][j
-v
[i
]]+w
[i
]);}}cout
<<ans
[n
][m
]<<endl
;}return 0;
}
這是沒有任何優(yōu)化的版本,也是最好理解背包問題的狀態(tài)轉移方程的代碼。
接下來我們對空間進行優(yōu)化。
#include<iostream>
#include<cstring>
#define maxn 1005
int ans
[maxn
];
using namespace std
;
int main() {int n
,m
;while(cin
>>n
>>m
) {memset(ans
,0,sizeof(ans
));while(n
--) {int v
,w
;cin
>>v
>>w
;for(int i
=m
;i
>=v
;i
--) {ans
[i
]=max(ans
[i
],ans
[i
-v
]+w
);}}cout
<<ans
[m
]<<endl
;}return 0;
}
二、完全背包。
這里我們可以對上面的0 1 背包一樣,對于每種可以當成是0 1背包的特殊情況,只不過每件物品是一樣的。
但是在這里要注意的是,共有n次0 1背包循環(huán),
#include<iostream>
#include<cstring>
#define maxn 1005
int ans
[maxn
];
using namespace std
;
int main()
{int n
,m
;while(cin
>>n
>>m
) {memset(ans
,0,sizeof(ans
));for(int i
=0;i
<n
;i
++) {int v
,w
;cin
>>v
>>w
;for (int j
=v
;j
<=m
;j
++)ans
[j
]=max(ans
[j
],ans
[j
-v
]+w
);}cout
<<ans
[m
]<<endl
;}return 0;}
三、多重背包。
這一道題當我們理解了前面兩道題目的是后發(fā)現并不難,只要在0 1背包加一重循環(huán)來枚舉數量。
于是我們有了下面的代碼。
#include<iostream>
#include<cstring>
#define maxn 1005
int ans
[maxn
];
using namespace std
;
int main() {int n
,m
;while(cin
>>n
>>m
) {memset(ans
,0,sizeof(ans
));while(n
--) {int v
,w
,s
;cin
>>v
>>w
>>s
;for(int i
=1;i
<=s
;i
++) {for(int j
=m
;j
>=i
*v
;j
--) {ans
[j
]=max(ans
[j
],ans
[j
-i
*v
]+i
*w
);}}}cout
<<ans
[m
]<<endl
;}return 0;
}
但是我們仔細想一想,這里的時間復雜度,nVs 已經到了1e9了,這里顯然會超時,想一想我們有沒有什么辦法來組合每一種物品的數量,
比如說15 我們可以變成,1,2,4,8的組合,而且1~15之間的任意數都可以是這幾個數的組合,于是我們想到了用二進制來拆分優(yōu)化數,這里的s就可以降到log2s了,
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#define maxn 100000
int ans
[maxn
];
struct each
{int v
,w
;
};
using namespace std
;
int main()
{int n
,m
;vector
<each
>a
;while(cin
>>n
>>m
) {memset(ans
,0,sizeof(ans
));a
.clear();for(int i
=1;i
<=n
;i
++) {int v1
,w1
,s
;cin
>>v1
>>w1
>>s
;for(int i
=1;i
<=s
;i
*=2) {s
-=i
;a
.push_back({i
*v1
,i
*w1
});}if(s
>0)a
.push_back({s
*v1
,s
*w1
});}for(int i
=0;i
<a
.size();i
++) {for(int j
=m
;j
>=a
[i
].v
;j
--)ans
[j
]=max(ans
[j
],ans
[j
-a
[i
].v
]+a
[i
].w
);}cout
<<ans
[m
]<<endl
;}return 0;
}
當然了對于數據沒有這么大的多重背包還是可以用第一個代碼來寫的
其實這里還有一個單調隊列優(yōu)化的,但是我水平有限就,沒給出代碼,了,有興趣可以自己去找一下代碼,
四、混合背包。
這組樣例的答案是8
其實這里就是前三種問題的匯總,我們可以在輸入的時候進行處理進行分類,下面是匯總了目前我會的上面的幾種方法的最優(yōu)解
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxn 10005
int ans
[maxn
];
using namespace std
;
struct each
{int v
,w
;
};
int main() {int n
,m
;vector
<each
>a
;while(cin
>>n
>>m
) {a
.clear();memset(ans
,0,sizeof(ans
));while(n
--) {int v1
,w1
,s
;cin
>>v1
>>w1
>>s
;if(s
==-1) {for(int j
=m
;j
>=v1
;j
--)ans
[j
]=max(ans
[j
],ans
[j
-v1
]+w1
);}else if(s
==0) {for(int j
=v1
;j
<=m
;j
++)ans
[j
]=max(ans
[j
],ans
[j
-v1
]+w1
);}else {for(int i
=1;i
<=s
;i
*=2) {s
-=i
;a
.push_back({i
*v1
,i
*w1
});}if(s
>0)a
.push_back({s
*v1
,s
*w1
});} }int longs
=a
.size();for(int i
=0;i
<longs
;i
++)for(int j
=m
;j
>=a
[i
].v
;j
--)ans
[j
]=max(ans
[j
],ans
[j
-a
[i
].v
]+a
[i
].w
);cout
<<ans
[m
]<<endl
; }return 0;
}
五、分組背包。
這里我們又可以吧分組背包看成是多重背包的一個特殊例子,每組個數有限制,但是只能取一個
#include<iostream>
#include<cstring>
#define maxn 105
int ans
[maxn
],v
[maxn
],w
[maxn
];
using namespace std
;
int main() {int n
,m
;while(cin
>>n
>>m
) {memset(ans
,0,sizeof(ans
));while(n
--) {int x
;cin
>>x
;for(int i
=0;i
<x
;i
++) cin
>>v
[i
]>>w
[i
];for(int i
=m
;i
>0;i
--)for(int j
=0;j
<x
;j
++)if(i
>=v
[j
]) ans
[i
]=max(ans
[i
],ans
[i
-v
[j
]]+w
[j
]);}cout
<<ans
[m
]<<endl
;}return 0;
}
剩下還有四個背包以后再更吧,就先寫到這。
總結
以上是生活随笔為你收集整理的背包问题——第一篇的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。