COJ1086

无聊的我水了一晚上的ACM水题。。。

  

1086: 超市购物

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 260   Solved: 112
[ Submit][ Status][ Web Board]

Description

        上次去超市扫荡回来的东西用完了,Staginner又得跑超市一趟,出发前他列了一张购物清单,打算去买K种不同的商品,每种买一件。到了超市,Staginner发现每种商品有N个品牌,每个品牌此商品的价格为Vi,对Staginner的作用值为Wi,他会从这N个品牌里面挑一个品牌买。这时,Staginner突然想起出门时只带了M元钱,又懒得去取钱了,所以不一定能买完K种商品,只好尽可能地让买的东西对自己的总作用值ans最大。

Input

 多组样例。

    第一行两个整数K,M代表Staginner想买的不同种类商品的数目和他带的钱 (0 < K <= 30, 0 < M <= 2000)
    以下输入分为K个部分,代表K种商品。
    每个部分第一行为一个数字N,代表第k种商品的N个品牌,N不大于10。之后跟着N行,每行两个数字,代表物品的价格Vi和作用值Wi。其中 0 < Vi < M。

Output

 输出Case #: 最大总作用值,每两个样例之间有一个空行。

Sample Input

3 100
3
50 600
20 700
30 800		
2
30 500
40 600	
1
60 200

2 500
2
200 1000
260 1200
1
280 300

Sample Output

Case 1: 1400





想到自己刚复习的贪心法,然后直接开始写
#include <stdio.h>

int k,m,ans;
struct Goods
{
    int kinds;
    int values[10];
    int weights[10];
    double factor[10];
    bool bought;
    void init()
    {
        scanf("%d",&kinds);
        for(int i=0;i<kinds;i++)
        {
              scanf("%d%d",&values[i],&weights[i]);
              factor[i]=weights[i]/1.0alues[i];
        }
        bought = false;

    }

    void sort()
    {
        for(int i=0;i<kinds;i++)
        {
            for(int j=i+1;j<kinds;j++)
            {
                if(values[j]>m)
                {
                    factor[j]=0;
                }
                if(factor[i]<factor[j])
                {
                    int tmp = factor[i];
                    factor[i]=factor[j];
                    factor[j]=tmp;
                    tmp = values[i];
                    values[i]=values[j];
                    values[j]=tmp;
                    tmp = weights[i];
                    weights[i]=weights[j];
                    weights[j]=tmp;
                }
            }
        }
    }
}G[30];


void sort()
{

    for(int i=0;i<k;i++)
    {
        if(G[i].bought == true)
            continue;
        G[i].sort();
        int max = G[i].factor[0];
        int max_index = i;
        for(int j=i+1;j<k;j++)
        {
            G[j].sort();
        }
        for(int j=i+1;j<k;j++)
        {
            if(G[j].bought == true)
            continue;
            if(G[j].factor[0]>max)
            {
                max_index = j;
                max = G[j].factor[0];
            }
        }
        Goods tmp = G[i];
        G[i] = G[max_index];
        G[max_index]=tmp;
        m-=G[i].values[0];
        ans+=G[i].weights[0];
        G[i].bought = true;
    }
}
int main()
{
    int kase = 0;
    while(scanf("%d%d",&k,&m)!=EOF)
    {
        kase ++;
        for(int i=0;i<k;i++)
        {
            G[i].init();

        }
        ans = 0;
        sort();
        printf("Case %d :%d\n",kase,ans);

    }

}

然后果断WA了

想了会,发现贪心法根本不能用,不符合贪心选择性质,他完全可以不取当前最优状态。
但是,这个问题确实具有最有子结构性质,因而可以给出动态规划解法。

#include<stdio.h>
#include<string.h>
int main()
{
    int K,M,i,j,u,t=0;
    int a[31],v[31][11],w[31][11],f[2001];
    while(scanf("%d %d",&K,&M)==2)
    {
        t++;
        memset(f,0,sizeof(f));
        for(i=0;i<K;i++)
        {
            scanf("%d",&a[i]);
            for(j=0;j<a[i];j++)
                scanf("%d %d",&v[i][j],&w[i][j]);
        }
        for(i=0;i<K;i++)
        {
            for(j=M;j>=0;j--)
            {
                for(u=0;u<a[i];u++)
                {
                    if(j-v[i][u]>=0)
                        f[j]=(f[j]>f[j-v[i][u]]+w[i][u] ? f[j] : f[j-v[i][u]]+w[i][u]);
                }
            }
        }
        printf("Case %d: %d\n\n",t,f[M]);
    }
    return 0;
}


代码来自:http://www.cnblogs.com/Yz81128/archive/2012/07/21/2602594.html
问题分类: 分组背包
作为一个连背包问题都没学完的ACM半途而废的弱渣,表示很羞愧。


这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。也就是说设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}
使用一维数组的伪代码如下:
for 所有的组k
    for v=V..0
        for 所有的i属于组k
            f[v]=max{f[v],f[v-c[i]]+w[i]}

详解见:http://www.cnblogs.com/Yz81128/archive/2012/07/21/2602594.html

睡了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值