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; }