1. 程式人生 > 實用技巧 >POJ 3093Margaritas on the River Walk 揹包DP

POJ 3093Margaritas on the River Walk 揹包DP

題目連結:http://poj.org/problem?id=3093

題目大意:給定一個容量為V(題目裡是D)的揹包和N(題目裡是V)件物品各自的體積,求有多少種方法使得揹包再也裝不下任何物品。

Sample Input

2
6 25
8 9 8 7 16 5
30 250
1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30

Sample Output

1 15
2 16509438

假設所有物品按照從小到大排序,列舉剩下物品的最小體積,則前面的所有物品都被放入揹包,當前的揹包空餘為v-sum[i],而為了擠佔掉所列舉的物品的空間,那麼我們就需要最小體積為v-sum[i]-a[i]+1的物品,最大為v-sum[i]的物品。我們將這個範圍內的物品體積的方案數直接加到總的ans中就行了,接下來就是物品體積方案數的轉移,dp[j]表示空間為j的可以又幾個方向轉移過來。那麼dp[j]=d[j]+dp[j-a[i]]。

以下是AC程式碼:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int mac=1e3+10;

int dp[mac],a[mac];

bool cmp(int x,int y){return x>y;}

int main()
{
    int t,n,m,test=0;
    scanf ("%d",&t);
    while (t--){
        int tot=0;
        scanf (
"%d%d",&n,&m); for (int i=1; i<=n; i++) scanf ("%d",&a[i]),tot+=a[i]; sort(a+1,a+1+n,cmp); test++; if (a[n]>m) {printf("%d 0\n",test);continue;} memset(dp,0,sizeof dp); dp[0]=1; int ans=0; for (int i=1; i<=n; i++){//
列舉剩下的最小a[i] tot-=a[i];//tot的當前值 int st=max(0,m-tot-a[i]+1);//m-tot,剩餘空間,不讓a[i]入包所需要佔據的最小空間 for (int j=st; j<=m-tot; j++)//佔據a[i]的空間 ans+=dp[j]; for (int j=m; j>=a[i]; j--)//空間為j的大小可以由幾個方向轉移過來 dp[j]+=dp[j-a[i]]; } printf("%d %d\n",test,ans); } return 0; }