Luogu P1021 邮票面值设计

gate

这题一看就是没有思路啊_(:3」∠)_

看起来像是类似货币系统那种背包,但是没有给定物品...

数据范围并不是很大,大概是O(搜索能过)

所以用dfs枚举每一种可能的邮票方案,再dp判断当前方案能表示的最大连续面值

dp:

枚举用几张邮票?显然不行。数组下标应该是面额,那么...

设b[i]为第i种邮票,f[j]为表示面额j所需的最少邮票数

状态转移方程:f[j] = min(f[j],f[j-b[i]]+1);

然后从1开始枚举,找到第一个f[j+1]>n的,那么j即为能表示的最大面额。

dfs:

在递归的过程中,记录并不断用dp更新最大值。

当枚举邮票数量=k时,判断是否能更新答案,并记录当前的邮票方案。

剪枝和一些范围的判断很重要!

首先,枚举邮票的面额时,要保证不重复而且连续。

范围是[上一张邮票的价值+1,当前能表示的最大面额+1]。

其次,dp中f[]数组下标的上限是k*所选最后一种邮票面额。

写memset的话就会TLE(亲测)

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;

const int maxn = 1e6+10;
const int INF = 0x3f3f3f3f;
int n,k,ans,a[maxn],b[maxn],f[maxn];

int dp(int x) {
    for(int i = 1; i <= b[x]*n; i++)
        f[i] = INF;
    for(int i = 1; i <= x; i++)
        for(int j = b[i]; j <= b[i]*n; j++)
            f[j] = min(f[j],f[j-b[i]]+1);
    for(int i = 0; i <= b[x]*n; i++)
        if(f[i+1] > n)
            return i;
}

void dfs(int x,int m) {
    if(x > k) {
        if(m <= ans) return;
        ans = m;
        for(int i = 1; i <= k; i++)
            a[i] = b[i];
        return;
    }
    for(int i = b[x-1]; i <= m+1; i++) {
        b[x] = i;
        dfs(x+1,dp(x));
    }
}

int main() {
    scanf("%d%d",&n,&k);
    dfs(1,0);
    for(int i = 1; i <= k; i++)
        printf("%d ",a[i]);
    printf("\nMAX=%d\n",ans);
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/mogeko/p/11778578.html