HDU3535AreYouBusy
题意&思路:
给时间T和工作集合n,有三种类型的集合:最少选一个工作,最多选一个工作,任选。问在T时间时完成工作的幸福度。
混合背包问题,有点难,看了题解才慢慢理解。
用dp[i][j]表示在第i个集合,时间为j的最大幸福度。
1、至少选一项。
将dp[i][j]全部赋值负无穷,这样就可以保证不会出现一个都不选的情况。
dp[i][j]=max{dp[i][j],dp[i-1][j-w[k]]+v[k],dp[i][j-w[k]]+v[k]},dp[i][j]表示不选当前工作;dp[i-1][j-w[k]]+v[k]表示第一次选本组工作,由于初始值为负无穷,所以第一次一定可以去一个工作;dp[i][j-w[k]]+v[k]表示选本组的工作,但不是第一次。
2、至多选一项。
最多选一项就是要么选,要么不选。
对于不选dp[i][j]=dp[i-1][j];对于选就是dp[i][j]=dp[i-1][j-w[k]]+v[k];
所以将dp[i][j]先赋值为dp[i-1][j]。
状态转移方程就是dp[i][j]=max{dp[i][j],dp[i-1][j-w[k]]+v[k]}。
3、任选
就是普通的01背包问题。
先将dp[i][j]赋值为dp[i-1][j]。
状态转移方程为dp[i][j]=max{dp[i][j],dp[i][j-w[k]]+v[k])。
代码:
#include<bits/stdc++.h>
const int N=1e6+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
int w[200],v[200],dp[200][200];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int n,t;
while(cin>>n>>t)
{
memset(dp,0,sizeof(dp));
int i,j,k;
for(i=1;i<=n;i++)
{
int m,p;
cin>>m>>p;
for(j=1;j<=m;j++)
cin>>w[j]>>v[j];
if(p==0)
{
for(j=0;j<=t;j++)
dp[i][j]=minn;
for(j=1;j<=m;j++)
for(k=t;k>=w[j];k--)
dp[i][k]=max(max(dp[i][k],dp[i][k-w[j]]+v[j]),dp[i-1][k-w[j]]+v[j]);
}
if(p==1)
{
for(j=0;j<=t;j++)
dp[i][j]=dp[i-1][j];
for(j=1;j<=m;j++)
for(k=t;k>=w[j];k--)
dp[i][k]=max(dp[i][k],dp[i-1][k-w[j]]+v[j]);
}
if(p==2)
{
for(j=0;j<=t;j++)
dp[i][j]=dp[i-1][j];
for(j=1;j<=m;j++)
for(k=t;k>=w[j];k--)
dp[i][k]=max(dp[i][k],dp[i][k-w[j]]+v[j]);
}
}
int ans=max(dp[n][t],-1);
cout<<ans<<endl;
}
return 0;
}