嘘~ 正在从服务器偷取页面 . . .

洛谷P1021 [NOIP1999 提高组] 邮票面值设计 题解


洛谷P1021 [NOIP1999 提高组] 邮票面值设计 题解

题目链接:P1021 [NOIP1999 提高组] 邮票面值设计

题意

给定一个信封,最多只允许粘贴 $N$ 张邮票,计算在给定 $K(N+K \le 15)$ 种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值 $\mathrm{MAX}$,使在 $1$ 至 $\mathrm{MAX}$ 之间的每一个邮资值都能得到。

例如,$N=3$,$K=2$,如果面值分别为 $1$ 分、$4$ 分,则在 $1\sim 6$ 分之间的每一个邮资值都能得到(当然还有 $8$ 分、$9$ 分和 $12$ 分);如果面值分别为 $1$ 分、$3$ 分,则在 $1\sim 7$ 分之间的每一个邮资值都能得到。可以验证当 $N=3$,$K=2$ 时,$7$ 分就是可以得到的连续的邮资最大值,所以 $\mathrm{MAX}=7$,面值分别为 $1$ 分、$3$ 分。

输入格式

$2$ 个整数,代表 $N$,$K$。

输出格式

输出共 $2$ 行。

第一行输出若干个数字,表示选择的面值,从小到大排序。

第二行,输出 MAX=S,$S$ 表示最大的面值。

本题的正解复杂度是错误的,不过我们还是来讲讲怎么做。

显然是暴搜。考虑暴力枚举 $k$ 个面值,然后用 dp 来检验。

时间复杂度 $\mathcal{O}(n^{k\operatorname{\uparrow\uparrow} 2})$ ,也就是 $\mathcal{O}(n^{k^k})$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(20))
#define M ((int)(1e6 + 15))

int n,k,res,val[N],ans[N],vis[M];
void check()
{
    const int Max = val[k] * n; 
    for(int i = 1; i <= Max; i++) vis[i] = INF;
    for(int i = 1; i <= k; i++) vis[val[i]] = 1;
    for(int sum = 1; sum <= Max; sum++)
    {
        if(vis[sum] == 1) continue;
        for(int i = 1; i <= k; i++)
        {
            if(sum < val[i]) break;
            down(vis[sum], vis[sum - val[i]] + 1);
        }
        if(vis[sum] > n)
        {
            if(--sum > res)
            {
                res = sum;
                memcpy(ans, val, (k + 1) * sizeof(int));
            }
            return ;
        }
    }
    if(Max > res)
    {
        res = Max;
        memcpy(ans, val, (k + 1) * sizeof(int));
    }
}
void dfs(int x)
{
    if(x == k + 1) return check();
    for(int i = val[x - 1] + 1; i <= val[x - 1] * n + 1; i++) {
        val[x] = i; dfs(x + 1);
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> k; dfs(1);
    for(int i = 1; i <= k; i++) {
        cout << ans[i] << " \n"[i == k];
    }
    cout << "MAX=" << res << '\n';
    return 0;
}

打表代码:(然而这垃圾题数据水的一匹)

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(20))
#define M ((int)(1e6 + 15))

int n,k,res,val[N],ans[N],vis[M];
void check()
{
    const int Max = val[k] * n; if(Max <= res) return;
    for(int i = 1; i <= Max; i++) vis[i] = INF;
    for(int i = 1; i <= k; i++) vis[val[i]] = 1;
    for(int sum = 1; sum <= Max; sum++)
    {
        if(vis[sum] == 1) continue;
        for(int i = 1; i <= k; i++)
        {
            if(sum < val[i]) break;
            down(vis[sum], vis[sum - val[i]] + 1);
        }
        if(vis[sum] > n)
        {
            if(--sum > res)
            {
                res = sum;
                memcpy(ans, val, (k + 1) * sizeof(int));
            }
            return ;
        }
    }
    if(Max > res)
    {
        res = Max;
        memcpy(ans, val, (k + 1) * sizeof(int));
    }
}
void dfs(int x)
{
    if(x == k + 1) return check();
    for(int i = val[x - 1] + 1; i <= val[x - 1] * n + 1; i++) {
        val[x] = i; dfs(x + 1);
    }
}
void clear() { res = 0; memset(ans, 0, sizeof(ans)); }
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    freopen("check.out","w",stdout);
    const int MaxL = 10;
    cout << "string ans[20][20] = {\n";
    for(n = 0; n <= MaxL; n++)
    {
        cout << "    { ";
        for(k = 0; n + k <= MaxL; k++)
        {
            clear(); cerr << "n = " << n << ", " << "k = " << k << '\n';
            if(k != 0) cout << ' '; cout << "\""; dfs(1);
            for(int i = 1; i <= k; i++) {
                cout << ans[i]; cout << ((i != k) ? " " : "\\n");
            }
            cout << "MAX=" << res << "\\n";
            cout << "\""; if(n + k != MaxL) cout << ',';
        }
        cout << " },\n";
    }
    cout << "};";
    return 0;
}

当然你也可以模拟退火做,反正我没写,不过我感觉蒙对的几率挺小。


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录