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