浙大 PAT 甲级 1068 Find More Coins 动态规划

这题和0-1背包问题很像,但增加了限制条件,满足准确值,而非求最大值。但动态规划这一基本思想都是适用的。

定义了几个数据结构

// 总金额为i的最大硬币数
int dp[101];
// 总金额为i,最大硬币数时的各硬币具体数量
int ans[101][101];
// 记录面值为i的硬币数
int coins[101];
// 记录能否达到总金额i
bool mark[101];
// 方便输出
vector<int> output;

dp[i]代表总金额为i的最多硬币数量,dp[i] 的值可以由dp[1]到dp[i-1]的值得到。即dp[i] = max(dp[1]+1,dp[2]+1,dp[3]+1,...,dp[i-1]+1),即,dp[i] = max(dp[i-j]+1),其中1<=j<=i-1,当然还有一些限制条件:

// 条件1 i-j金额能够凑到,即mark[i-j]==true
// 条件2 i-j金额加上一个j硬币的总数大于等于(等于时根据题意选择最优)当前数量
// 条件3 j硬币数量足够

AC代码如下:

#include<stdio.h>
#include<memory.h>
#include<vector>
using namespace std;

// 总金额为i的最大硬币数
int dp[101];
// 总金额为i,最大硬币数时的各硬币具体数量
int ans[101][101];
// 记录面值为i的硬币数
int coins[101];
// 记录能否达到总金额i
bool mark[101];
// 方便输出
vector<int> output;

int main()
{
    memset(coins, 0, sizeof(int) * 101);
    for (int i = 0; i < 101; i++)
    {
        for (int j = 0; j < 101; j++)
        {
            ans[i][j] = 0;
        }
    }
    memset(mark, 0, sizeof(bool) * 101);
    int N, M;
    scanf("%d%d", &N, &M);
    for (int i = 0; i < N; i++)
    {
        int coin;
        scanf("%d", &coin);
        if (coin <= M)
        {
            coins[coin]++;
        }
    }
    for (int i = 1; i <= M; i++)
    {
        mark[i] = false;
        // 有面值为i的硬币
        if (coins[i] > 0)
        {
            dp[i] = 1;
            mark[i] = true;
            ans[i][i] = 1;
        }
        for (int j = 1; j < i; j++)
        {
            // 条件1 i-j金额能够凑到
            // 条件2 i-j金额加上一个j硬币的总数大于当前数量
            // 条件3 j硬币数量足够
            if (mark[i - j] == true && coins[j] - ans[i - j][j] > 0)
            {
                int flag;
                if (dp[i - j] + 1 > dp[i])
                {
                    flag = true;
                }
                if (dp[i - j] + 1 == dp[i])
                {
                    for (int k = 1; k < 101; k++)
                    {
                        if (k == j)
                        {
                            if (ans[i - j][j] + 1 > ans[i][j])
                            {
                                flag = true;
                                break;
                            }
                            if (ans[i - j][j] + 1 < ans[i][j])
                            {
                                flag = false;
                                break;
                            }
                        }
                        if (ans[i - j][k] < ans[i][k])
                        {
                            flag = false;
                            break;
                        }
                        if (ans[i - j][k] > ans[i][k])
                        {
                            flag = true;
                            break;
                        }
                    }
                }
                if (dp[i - j] + 1 < dp[i])
                {
                    flag = false;
                }
                if (flag)
                {
                    mark[i] = true;
                    dp[i] = dp[i - j] + 1;
                    for (int k = 1; k < 101; k++)
                    {
                        ans[i][k] = ans[i - j][k];
                    }
                    ans[i][j]++;
                }
            }
        }
    }
    if (mark[M] == false)
    {
        printf("No Solution\n");
    }
    else
    {
        for (int i = 1; i <= M; i++)
        {
            for (int j = 0; j < ans[M][i]; j++)
            {
                output.push_back(i);
            }
        }
        for (int i = 0; i < output.size() - 1; i++)
        {
            printf("%d ", output[i]);
        }
        printf("%d\n", output.back());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值