timus 1658. Sum of Digits URAL 解题报告 DP 数字位数和……

timus  1658. Sum of Digits   URAL  解题报告   DP 数字位数和……

给每位上的数的和,和平方之后的和……  然后求出这个数的最小值,不能 的话输出IMPOSSIBLE……
郁闷了好久,这个题一直不会做,因为既要照顾位数要少,还要照顾小数在前面,而且状态转移的话还不好想,因为当时想到,一个数位数和增加,平方和增加的很巧妙,说不定是从把多余的和平均到各个位数上,由于每个位上的基数不同,那么增加相同的数发挥的作用也不相同;那么好麻烦啊,后来看了别人的解题报告还一直怀疑有错误,非常郁闷,还一直找bug,后来才明白,这种情况不会出现,我当初思考的是有木有这样一种状况,就是位数和还有平方和变大之后位数没有改变,只不过把多余的和转移到不同的位上,比如说,和为i,平方和为j,变成i+num,j+num*num之后,位数没有改变只不过把多余的num分到不同的位上,
f[9][41] = f[3][5],而不是f[9][41]=f[9-6][41-36]+1   虽然我现在找到的代码成功解决了这个问题,但是没有想明白为什么解决了这个问题……
各位大神,有明白的解释下,谢谢!
参考博客

下面的代码再解释下,先把最少的位数确定,然后每一位上的数字可以互换的,所以排序输出既可以了……
#include<iostream>
#include<string>
#include<sstream>
#include<string.h>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
using namespace std;
#define INF 0x7ffffff


string str;
int dp[910][8110];
int dig[910][8110];
int ans[110],tot;
void init()
{
    for(int i=0;i<901;++i)
    {
        for(int j=0;j<8101;++j)
        {
            dp[i][j]=INF;
        }
    }dp[0][0]=0;
    for(int i=1;i<901;++i)
    {
        for(int j=i;j<8101;++j)
        {
            for(int k=1;k<10;++k)
            {
                int tmp;
                if(i>=k&&j>=k*k&&(tmp=dp[i-k][j-k*k]+1)<dp[i][j])
                {
                    dp[i][j]=tmp;
                    dig[i][j]=k;
                }
            }
        }
    }

}

void write(int s1,int s2,int cnt)
{
    if(cnt==0)return;
    ans[cnt]=dig[s1][s2];
    write(s1-ans[cnt],s2-ans[cnt]*ans[cnt],cnt-1);
}
int main()
{
    init();
    int t,s1,s2;
    cin>>t;
    while(t--)
    {
        scanf("%d %d",&s1,&s2);
       // if(dp[s1][s2]>100)puts("okokokokok");
        if(s1>900||s2>8100||dp[s1][s2]>100)
        {///主题题目中所给的位数是100位,所以各位和不可能超过900……
            printf("No solution\n");continue;
        }
        tot=dp[s1][s2];

        write(s1,s2,tot);
        sort(ans+1,ans+tot+1);///找出最短几位就可以满足之后然后把数字排序即可
        for(int i=1;i<tot+1;++i)
        {
            printf("%d",ans[i]);
        }cout<<endl;
    }


}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值