http://acm.hdu.edu.cn/showproblem.php?pid=4341
黄金矿工的游戏,给n个点,每个点有花费,价值,让最少的时间内选出最大价值的物品集合。
有个问题就是共线的点,我们必须先选取 前面的,才能选后面的,换句话说,要选 共线的第3个点,必须把前两个都选了
也就是说,所有的点里,只有共线的点之间才有约束,非共线的点 互不相干,因此可以按极角序 分组,共线的放在一组,
每一组 存的几个node,是代表前缀耗时,和前缀价值,表示选1到i个物品的状态。
因此每组只能选一个 node。
因此把问题转化为01背包,只不过原来的背包之间的转移,变成组与组的转移。
每一组里,只能选 西格玛【i】个物品。 的一个
数组用滚动或一维存一下就好l
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include<cstdlib>
using namespace std;
struct Point
{
int x,y,w,v;
}point[210];
struct node
{
int v,w;
};
int w[210],v[210];
vector<node> a[210];
int n,m;
int f[2][40010];
bool cmp(Point p,Point q)
{
double a1=atan2((double) p.y,(double) p.x),a2=atan2((double) q.y,(double) q.x);
if (fabs(a1-a2)<1e-6)
return p.x*p.x+p.y*p.y<q.x*q.x+q.y*q.y;
else
return a1<a2;
}
int main()
{
//freopen("xl_in.txt","r",stdin);
int cas=0;
while (scanf("%d%d",&n,&m)!=EOF)
{
for (int i=1; i<=n; i++)
{
scanf("%d%d",&point[i].x,&point[i].y);
scanf("%d%d",&point[i].w,&point[i].v);
}
sort(point+1,point+1+n,cmp);
for (int i=1; i<=n; i++)
a[i].clear();
int num=1;
node tmp1;
tmp1.v=point[1].v;
tmp1.w=point[1].w;
a[1].push_back(tmp1);
for (int i=2; i<=n; i++)
{
if (point[i].x*point[i-1].y-point[i].y*point[i-1].x==0)
{
node tmp=a[num][a[num].size()-1];
tmp.v+=point[i].v;
tmp.w+=point[i].w;
a[num].push_back(tmp);
}
else
{
num++;
tmp1.v=point[i].v;
tmp1.w=point[i].w;
a[num].push_back(tmp1);
}
}
memset(f,0,sizeof(f));
for (int i=1; i<=num; i++)
{
for (int j=0; j<=m; j++)
{
f[i%2][j]=f[(i-1)%2][j];
for (int k=0; k<a[i].size(); k++)
{
if (j>=a[i][k].w)
f[i%2][j]=max(f[i%2][j],f[(i-1)%2][j-a[i][k].w]+a[i][k].v);
}
}
}
int ans=0;
for (int j=0; j<=m; j++)
ans=max(ans,f[num%2][j]);
printf("Case %d: %d\n",++cas,ans);
}
return 0;
}