这是一道分组背包的题,这题开始乍一看以为是01背包,但是看了数据才明白是一道分组背包。
分组背包其实是一种变相的01背包。
只不过01背包是每件物品只有一件,可以选择取或不取。
分组背包则是有若干类物品,,每类物品中有若干个各不相同的物品,但是每类物品里只能选一样.................
他的状态转移方程是这样的:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}
所以这题有两个难点,一个是把斜率相同的金块归类到一个类里面,
而后就是实现这个状态方程:
一言不合直接上代码;
(有点遗憾的就是太耗时了,想不到优化的方法)
#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
#define N 220
struct Node
{
int x,y;
int v,t;
}node[N];
bool cmp(Node p1,Node p2)
{
int x1=p1.x;
int y1=p1.y;
int x2=p2.x;
int y2=p2.y;
if(y1*x2==y2*x1) //把斜率相同的金块排序,紧挨在一起
return y1<y2; //注意理解一下,下面归类有用
return y1*x2<y2*x1; //这里依据什么排序其实无所谓,因为主要是实现让斜率相同的金块紧挨排序
}
int group[N][N];
int cnt;
int dp[50000];
int solve(int T)
{
int t,i,j,k;
memset(dp,0,sizeof(dp));
for(i=0;i<=cnt;i++)
{
for(t=T;t>=0;t--)
{
for(j=1;j<=group[i][0];j++)//group[i][0]代表的是第i类的有几个物品
{
k=group[i][j];
if(t-node[k].t>=0)
{
dp[t]=max(dp[t],dp[t-node[k].t]+node[k].v);//状态转移方程
}
}
}
}
return dp[T];
}
int main()
{
int n,T,i;
int c=0;
while(~scanf("%d %d",&n,&T))
{
for(i=0;i<n;i++)
scanf("%d %d %d %d",&node[i].x,&node[i].y,&node[i].t,&node[i].v);
sort(node,node+n,cmp);
cnt=-1;
for(i=0;i<n;i++)
{
cnt++;
group[cnt][0]=0;
group[cnt][++group[cnt][0]]=i;//每类物品至少有一个以上的物品,标记第一个物品是第几个物品
int x1=node[i].x;
int y1=node[i].y;
int a=node[i].t;
int b=node[i].v;
for(i+=1;i<n;i++)//因为上面排序使斜率相同的金块紧挨,所以只要在当前金块往下找找到斜率
{
int x2=node[i].x;
int y2=node[i].y;
a+=node[i].t;
b+=node[i].v;
if(y1*x2==y2*x1)
{
group[cnt][++group[cnt][0]]=i;////斜率相同,为同一类的金块,把第几块位置标记入类
node[i].t=a;
node[i].v=b;
}
else//找到斜率不一样的就说明下面没有斜率相同的金块了,跳出
{
i--;
break;
}
}
}
printf("Case %d: ",++c);
printf("%d\n",solve(T));
}
return 0;
}