三種背包,每組至少、至多取一個或隨意取多少
注釋在代碼中
#include<cstdio>#include<cstring>int dp[110][110];int max(int a,int b){return a>b?a:b;}int main(){int n,T,i,j,k,m,s,w,v;while(scanf("%d%d",&n,&T)!=EOF){memset(dp,0,sizeof(dp));for(i=1;i<=n;i++){scanf("%d%d",&m,&s);if(s==0)//至少選一個{for(j=0;j<=T;j++)dp[i][j]=-1;//保證下面會有一個被選中for(j=1;j<=m;j++){scanf("%d%d",&w,&v);for(k=T;k>=w;k--){//下面這兩句話為什麼一換就錯啊,不懂了。。。。if(dp[i][k-w]!=-1) dp[i][k]=max(dp[i][k],dp[i][k-w]+v);//如果dp[i][k-w]存在,則表示i組已經取過了,就相當與在去過的基礎上再取,即,至少一個if(dp[i-1][k-w]!=-1)dp[i][k]=max(dp[i-1][k-w]+v,dp[i][k]);//保證一定取一個}}}if(s==1)//至多選一個{for(j=0;j<=T;j++)dp[i][j]=dp[i-1][j];for(j=1;j<=m;j++){scanf("%d%d",&w,&v);for(k=T;k>=w;k--){if(dp[i-1][k-w]!=-1)dp[i][k]=max(dp[i][k],dp[i-1][k-w]+v);}}}if(s==2)//隨意選{for(j=0;j<=T;j++)dp[i][j]=dp[i-1][j];for(j=1;j<=m;j++){scanf("%d%d",&w,&v);for(k=T;k>=w;k--){if(dp[i][k-w]!=-1)dp[i][k]=max(dp[i][k],dp[i][k-w]+v);}}}}int ans=-1;for(i=0;i<=T;i++){if(dp[n][i]>ans)ans=dp[n][i];}printf("%d\n",ans);}return 0;}
關於代碼中兩個迴圈不能對調的問題,做出如下解釋
考慮到某件物品的體積可能為0,所以先從上一層更新過來,再更新當前一層的話,這件物品可能會被選擇兩次!!!!這是不允許的
if(s==0)//至少選一個 { for(j=0;j<=T;j++) dp[i][j]=-1;//保證下面會有一個被選中 for(j=1;j<=m;j++) { scanf("%d%d",&w,&v); for(k=T;k>=w;k--) { if(dp[i-1][k-w]!=-1) dp[i][k]=max(dp[i-1][k-w]+v,dp[i][k]); //假如更新成功,那麼dp[i][k]所代表的狀態就已經加入過一個當前物品了 if(dp[i][k-w]!=-1) dp[i][k]=max(dp[i][k],dp[i][k-w]+v);//體積為0,就可能會再一次選擇這個物品,發生自己更新自己的慘劇
//如果迴圈的順序如上所示,那麼這個物品就有可能被選擇兩次了,迴圈順序換一下就不會了
} } }
if(s==0)//至少選一個
{
for(j=0;j<=T;j++)
dp[i][j]=-inf;//保證下面會有一個被選中
for(j=1;j<=m;j++)
{
scanf("%d%d",&w,&v);
for(k=T;k>=w;k--)
{
dp[i][k]=max(dp[i][k],max(dp[i][k-w],dp[i-1][k-w])+v);//這樣寫就不會出現上述的問題了
}
}
}