牛客练习赛7:B-购物 (dp)

题目链接:https://www.nowcoder.com/acm/contest/38/B


题目描述:

给出N,M,有N天,每天店里都生产M颗糖果,这N天要去商店买

糖果,但是有一个条件,如果某天买的糖果数是K个,则需要额外

花费K*K的钱,而且要保证每天都有糖果吃。问购物的最小花费。


解题思路:

贪心+dp.

从题目中提取信息。每天都要买糖果吃,则前i天买的糖果数量

至少是i,而且最少买N颗糖果就可以满足条件,为了使花钱最少,

某天买k件的话,希望是当天糖果的前k小。所以对于每天给出的

M个糖果,按价格从小到大排序。这样就能计算出某天买K件的最

小花费。然后设立数组,dp[i][j]代表的含义是前i天至少买j件的最

小花费。由于前i天至少买i个,所以对于前i天,j的范围是从i~N,

然后我们可以枚举第i天买多少件,然后其状态可以由前i-1天买

糖果数得来。由于第1天必须买,而剩余N天可以有买的情况,

和不买的情况,所以可以把第一天抽出来考虑。

比赛的时候我没有把第一天单独抽出来,导致思路极度混乱,

我来我就单独处理第一天,其他的都按他们的条件处理,然后

就直接交过了,真的应该早点把他抽出来。


AC代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
 
using namespace std;
 
const int inf = 0x3f3f3f3f;
const int maxn = 305;
int dp[maxn][maxn];     ///dp[i][j]前i天买j件的最少花费
int temp[maxn];         ///临时存放第i天糖果价格的数组
int price[maxn][maxn];  ///price[i][j]第i天买j件的最小花费
///需要N天,则需要N个糖果
int main() {
    int N,M;
    while(~scanf("%d%d",&N,&M)) {
        for(int i = 1; i <= N; i++) {
            for(int j = 1; j <= M; j++) {
                scanf("%d",&temp[j]);
            }
            sort(temp+1,temp+1+M);  ///对每组价格排序
            price[i][0] = 0;
            for(int j = 1; j <= M; j++) {
                price[i][j] = price[i][j-1]+temp[j]-(j-1)*(j-1)+j*j;
            }
        }
        int Max = max(M,N);
        for(int i = 0; i <= N; i++) {
            for(int j = 0; j <= Max; j++) {
                    if(i == 0)
                        dp[i][j] = 0;
                    else
                        dp[i][j] = inf;
            }
        }
        /**由于每天必须有糖果吃,因此第1天必须买,
        而剩余几天可能买,可能不买,第一天与其他
        天情况考虑不一致,单独抽出来求**/
        for(int j = 1; j <= M; j++) {
            dp[1][j] = price[1][j];
        }
        for(int i = 2; i <= N; i++) {
           ///前i天买k个糖果,由于前i天至少买i个糖果,所以k>=i
           for(int k = N; k >= i; k--) {
                ///枚举第i天买了多少糖果,前i-1天至少买i-1个糖,所以k-j>=i-1
                for(int j = 0; j <= M; j++) {
                    if(k-j >= i-1) {
                        dp[i][k] = min(dp[i][k],dp[i-1][k-j]+price[i][j]);
                    }
                }
           }
        }
        printf("%d\n",dp[N][N]);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值