SGU 390 Tickets(数位DP)

题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=390

 

题意:有一位售票员给乘客售票。对于每位乘客,他会卖出多张连续的票,直到已卖出的票的 编号的数位之和不小于给定的正数 k。然后他会按照相同的规则给下一位乘客售票。初始时, 售票员持有的票的编号是从 L 到 R 的连续整数。请你求出,售票员可以售票给多少位乘客。

 

思路:每次dfs时记录当前的数字之和sum及前一个子树剩余的数字rem,如果sum + rem大于k就把剩余数字清零

 

 

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <functional>
#include <utility>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <set>
#include <cmath>
#include <stdlib.h>
#include <climits>

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#pragma comment (linker, "/STACK:1024000000,1024000000")

typedef long long ll;

using namespace std;

const int maxn = 20;
const int maxk = 1010;

struct node
{
    ll cnt, rem;
    node(ll cnt = 0, ll rem = 0) : cnt(cnt), rem(rem) {}
    node operator += (node nxt)
    {
        cnt += nxt.cnt;
        rem = nxt.rem;
        return *this;
    }
} dp[maxn][maxn * 10][maxk];

bool vis[maxn][maxn * 10][maxk];
int dl[maxn], dr[maxn];

ll l, r, k;

node dfs(int h, ll sum, ll rem, bool lim1, bool lim2)
{
    if (h == 0)
    {
        if (sum + rem >= k)
            return node(1, 0);
        return node(0, sum + rem);
    }
    if (vis[h][sum][rem] && !lim1 && !lim2)
        return dp[h][sum][rem];

    node ans(0, rem);
    int low = lim1 ? dl[h] : 0;
    int upp = lim2 ? dr[h] : 9;
    for (int i = low; i <= upp; i++)
        ans += dfs(h - 1, sum + i, ans.rem, lim1 && i == low, lim2 && i == upp);
    if (!lim1 && !lim2)
    {
        dp[h][sum][rem] = ans;
        vis[h][sum][rem] = 1;
    }
    return ans;
}

int main()
{
    cin >> l >> r >> k;
    int cnt = 1;
    while (l)
    {
        dl[cnt++] = l % 10;
        l /= 10;
    }
    cnt = 1;
    while (r)
    {
        dr[cnt++] = r % 10;
        r /= 10;
    }
    node ans = dfs(cnt, 0, 0, 1, 1);
    cout << ans.cnt << endl;
    return 0;
}



 

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值