题目链接
思路摘抄自大佬博客
首先如果存在最优解, 我们可以互换不同工作集合的处理顺序, 依然能得到最优解. 那么我们下面只需要处理每个单独的工作集合即可.
令dp[i][j]==x表示处理完前i组工作集,所花时间<=j时的快乐值为x。每得到一组工作就进行一次DP,所以dp[i]为第i组的结果。下面对三种情况进行讨论。
1.该集合内至少要选1件工作时. 要保证至少选1个第i类工作, 可以从第i-1类的结果dp[i-1]来更新dp[i].也可以用 01背包的思想, 从本类的前一个工作更新后一个工作.
初始化:dp[i]全为负无穷.(即-INF)
状态转移方程为:
dp[i][k]=max{dp[i][k],dp[i-1][k-cost[j]]+val[k],dp[i][k-cost[j]]+val[j] }
2.该集合内最多选1件工作时. 只能从上一层的结果dp[i-1]来更新dp[i]了.(想想为什么)
初始化:dp[i]==dp[i-1].状态转移方程为dp[i][k]=max{dp[i][k],dp[i-1][k-cost[j]]+val[k]}.
3. 该集合内工作可以随便选. 这就是1个普通的01背包问题了.
初始化:dp[i]==dp[i-1].
状态转移方程为:
dp[i][k]=max{dp[i][k],dp[i-1][k-cost[j]]+val[k],dp[i][k-cost[j]]+val[j] }
最终所求:dp[n][t]的值
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
int n;//n组工作
int t;//t分钟
int dp[110][110];//处理前i组工作,所花时间<=j的幸福点
int cost[110];//成本
int val[110];//幸福点
int main()
{
//freopen("in.txt","r",stdin);
int i,j,k;
while(scanf("%d%d",&n,&t)==2)
{
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
int m,s;
scanf("%d%d",&m,&s);//m个作业,类型s
for(k=1;k<=m;k++)
scanf("%d%d",&cost[k],&val[k]);
if(s==0)//至少选一个的01背包问题
{
for(j=0;j<=t;j++)dp[i][j]=-INF;//初始化
for(k=1;k<=m;k++)
for(j=t;j>=cost[k];j--)
{
dp[i][j]=max(dp[i][j],dp[i][j-cost[k]]+val[k]);
dp[i][j]=max(dp[i][j],dp[i-1][j-cost[k]]+val[k]);
//交换顺序会错
}
}
else if(s==1)//至多选1个的01背包问题
{
for(j=0;j<=t;j++)dp[i][j]=dp[i-1][j];
for(k=1;k<=m;k++)
for(j=t;j>=cost[k];j--)//无所谓顺序
{
dp[i][j]=max(dp[i][j],dp[i-1][j-cost[k]]+val[k]);
}
}
else if(s==2)//随便选的01背包
{
for(j=0;j<=t;j++)dp[i][j]=dp[i-1][j];
for(k=1;k<=m;k++)
for(j=t;j>=cost[k];j--)//只能逆序
dp[i][j]=max(dp[i][j],dp[i][j-cost[k]]+val[k]);
}
}
int ans=max(dp[n][t],-1);
printf("%d\n",ans);
}
return 0;
}