2147: Digit

看到题目首先想到了数位dp

观察发现,从低位向高位很好推.

令fijkg表示第i个位置,进位值为j 数位和为k 乘d后数位和为g得情况是否成立...

稍微算一算100 * 10 * 1000 * 1000肯定爆掉了...

考虑优化,令fijk表示能凑成进位值为i 数位和为j 乘d后数位和为k最短得长度...

那么这样复杂度就在1000w 可以接受...

因为要保证得到得数是最小得情况,那么只能从高位到低位枚举。

而且每次选出来的值均对原本乘积和得值有影响,那么还要更新一下...

理论上来说不是很难,但是本沙茶还是写了一天...

c++代码如下:

#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <ctime>
#include <cstring>
#include <iostream>
#include <algorithm>
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define lowbit(x) x&(-x)
#define PA pair<int, int>
#define MK make_pair
#define rep(i,x,y) for(register int i = x; i <= y; ++ i)
#define repd(i,x,y) for(register int i = x; i >= y; -- i)
using namespace std;
typedef long long ll;
template<typename T>inline void read(T&x)
{
    x = 0;char c;int sign = 1;
    do { c = getchar(); if(c == '-') sign = -1; }while(!isdigit(c));
    do { x = x * 10 + c - '0'; c = getchar(); }while(isdigit(c));
    x *= sign;
}
  
int n,s1,s2,d;
int f[10][901][910],nw[101],t[101];
struct Str { int lst,num1,num2; };

inline void bfs()
{
	queue<Str>q; 
	q.push((Str){0,0,0});
	f[0][0][0] = 0;
    while(!q.empty())
	{
		Str a = q.front();q.pop();
		rep(i,0,9) if(a.num1 + i <= s1 && a.num2 + (i*d + a.lst)%10 <= s2 && f[(a.lst + i*d)/10][a.num1 + i][a.num2 + (i*d + a.lst)%10] > f[a.lst][a.num1][a.num2] + 1){
			f[(a.lst + i*d)/10][a.num1 + i][a.num2 + (i*d + a.lst)%10] = f[a.lst][a.num1][a.num2] + 1; 
			q.push((Str){(a.lst + i*d)/10,a.num1 + i,a.num2 + (i*d + a.lst)%10});
		}
	}
}
void get(int now,int s1,int p)
{
    if(now == n + 1) exit(0);
    rep(k,0,9)
    {
        rep(j,0,9)
        {
            int q = now,z = p;
            t[now] = k*d + j;
            while(t[q] + nw[q] >= 10)
            {
                t[q - 1] = (t[q] + nw[q]) / 10;
				t[q] = (t[q] + nw[q]) %10;
                z += t[q] - nw[q];
				--q;
            }
            z += t[q];
            if(s1 >= k && s2 >= z && f[j][s1 - k][s2 - z] + now <= n)
            {
            	q = now;
                nw[q] = k*d; p += nw[q]; 
                while(nw[q] >= 10)
                {
                	p -= nw[q - 1] + nw[q];
					nw[q - 1] += nw[q]/10;nw[q] %= 10;
					p += nw[q - 1] + nw[q];
					--q;
				}
                printf("%d",k);
                get(now + 1,s1 - k,p);
            }
        }
    }
}
int main()
{
    memset(f,0x3f,sizeof f);
    read(n);read(s1);read(s2);read(d);
    
    bfs();
    
    int z = 0;
    rep(i,1,9)
    {
        rep(j,0,9)
            if(s1 >= i && s2 - (i*d + j)%10 - (i*d + j)/10 >= 0 && f[j][s1 - i][s2 - (i*d + j)%10 - (i*d + j)/10] < n)
            {
                z = i;
                nw[1] = (i * d)%10;
                nw[0] = (i * d)/10;
                break;
            }
        if(z) break;
    }
        
    if(!z) return puts("-1");
    
    cout << z;
    get(2,s1 - z,nw[1] + nw[0]);
    
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值