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;
}
}