HDU 4341-分组背包/01背包

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值