[英]Summation of a number made up of 4 5 6
我們給出三個整數x,y和z。 你必須找到所有數字的總和,其數字僅由4,5和6組成,十進制表示最多為x四,十進制表示最多為y五,十進制表示最多為z六
我正在使用Describe Here這個概念
我的代碼:
// fact[i] is i!
for(int i=0;i<=x;i++)
for(int j=0;j<=y;j++)
for(int k=0;k<=z;k++){
int t = i+j+k;
if(t==0) continue;
long ways = fact[t-1];
long pow = (long) Math.pow(10,t-1);
long rep=0;
if(i!=0){
rep = fact[j]*fact[k];
if(i>0) rep*=fact[i-1];
o+= 4*pow*(ways/rep);
}
if(j!=0){
rep = fact[i]*fact[k];
if(j>0) rep*=fact[j-1];
o+= 5*pow*(ways/rep);
}
if(k!=0){
rep = fact[i]*fact[j];
if(k>0) rep*=fact[k-1];
o+= 6*pow*(ways/rep);
}
}
但我得到錯誤的答案x=1 , y=1 and z=1
我得到3315
而正確答案是3675
。
請幫我找出錯誤。
4+5+6+45+54+56+65+46+64+456+465+546+564+645+654=3675
問題不在於您的代碼,而在於您的邏輯:設S是僅由數字4,5和6組成的數字集。您想要計算SUM(S)。 但是因為你只考慮這些數字的第一個數字,所以你實際上是計算SUM(s中的s,s - s%10 ^ floor(log10(s)))。
你這樣做是正確的,因為
4 + 5 + 6 + 40 + 50 + 50 + 60 + 40 + 60 + 400 + 400
+ 500 + 500 + 600 + 600 = 3315
簡而言之,您需要做的就是應用下面的用戶גלעדברקן的方法來修復您的代碼。 它將導致O(xyz(x + y + z))算法,並且可以通過看到SUM(i = 0到t-1,10 ^ i)=(10 ^ t - 1)來改進為O(xyz)。 )/ 9,所以在你的代碼中改變一行真的夠了:
// was: long pow = (long) Math.pow(10,t-1);
long pow = (long) (Math.pow(10,t)-1) / 9;
還有一個非常簡單的O(xyz)時間+空間方法,使用動態編程,只使用最少的數學和組合:讓g(x,y,z)為元組(count,sum),其中count是4的數-5-6個數字由正好 x四個,五個和五個六個組成。 總和是他們的總和。 然后我們有以下重復:
using ll=long long;
pair<ll, ll> g(int x, int y, int z) {
if (min(x,min(y,z)) < 0)
return {0,0};
if (max(x,max(y,z)) == 0)
return {1,0};
pair<ll, ll> result(0, 0);
for (int d: { 4, 5, 6 }) {
auto rest = g(x - (d==4), y - (d==5), z - (d==6));
result.first += rest.first;
result.second += 10*rest.second + rest.first*d;
}
return result;
}
int main() {
ll res = 0;
// sum up the results for all tuples (i,j,k) with i <= x, j <= y, k <= z
for (int i = 0; i <= x; ++i)
for (int j = 0; j <= y; ++j)
for (int k = 0; k <= z; ++k)
res += g(i, j, k).second;
cout << res << endl;
}
我們可以向g添加memoization以避免計算結果兩次,從而產生多項式時間算法而無需組合洞察。
對於你可以使用超過3位數的情況,這很容易推廣,如gen-ys的回答所示 。 對於您對數字形狀有更復雜限制的情況,也可以推廣。 如果要通過將其與另一種通用DP方法相結合來對給定范圍內的數字求和,它甚至可以推廣。
編輯:還有一種方法可以直接描述你的函數f(x,y,z) ,4-5-6數的總和最多包含x fours,y fives和z sixes。 你需要包含 - 排除。 例如,對於我們的計數部分
c(x,y,z)= c(x-1,y,z)+ c(x,y-1,z)+ c(x,y,z-1) - c(x-1,y- 1,z) - c(x-1,y,z-1) - c(x,y-1,z-1)+ c(x-1,y-1,z-1)
總和稍微復雜一些。
在Python 3中:
def sumcalc(x,y,z):
if x < 0 or y < 0 or z < 0: return -1
import itertools
sum = 0
for i, j, k in itertools.product(range(x + 1), range(y + 1), range(z + 1)):
e = (('4' * i) + ('5' * j) + ('6' * k))
if e:
perms = [''.join(p) for p in itertools.permutations(e)]
for i in set(perms): sum += int(i)
return sum
這種方法很簡單,可以與大多數編程語言一起使用,不一定包括類似的語法糖(如果有的話)。 基本步驟是:
對於給定的整數x,y和z all> = 0,為所有組合中的每一個寫入一個字符串,忽略從0到x出現的'4'的順序,從0到y出現的'5'和從0到z的'6'發生。 (但是,組合是按順序生成的,以確保完整性。)
對於(1)中產生的每個字符串,生成其字符的所有唯一和非空排列。
對於(2)中產生的每個字符串排列,將其轉換為整數並將其添加到總和中。
Python 3整數具有無限的精度,因此無需拖動Long或BigInteger類型來改進它。
你的邏輯幾乎是正確的。 你剛才忘了,每個數字在每個位置(出現pow
的每個配置在計算) (i,j,k)
您可以通過添加額外的循環輕松修復代碼:
for(int i=0;i<=x;i++)
for(int j=0;j<=y;j++)
for(int k=0;k<=z;k++){
int t = i+j+k;
for (int p=0; p<t; p++){ // added loop
long ways = fact[t-1];
long pow = (long) Math.pow(10,p); // changed
或者,甚至更好,感謝Niklas B.的評論:而不是添加一個循環,只需分配pow
pow = (long) Math.pow(10,t - 1) / 9
編輯:我意識到帖子鏈接描述了相同的東西。 我誤認為它與幾天前漂浮在SO上的類似問題有關,而這個問題完全不同。 因此刪除了它,但后來取消刪除,因為它可以解釋OP中代碼中的錯誤。
這可以作為具有O(x y z)復雜度的組合問題來解決。
讓我們將問題分成兩部分:
A部分:求出包含正好x 4s,y 5s和z 6s的數字之和。 這很簡單:
設數如下: _ _ _..._ 4 _ ... _
,其中顯示的4顯示在10^k
位置。 其余的數字可以排列在(x+y+z-1)! / ((x-1)! * y! * z!)
(x+y+z-1)! / ((x-1)! * y! * z!)
方式。 因此,在這個位置貢獻4的總和是4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!)
4 * 10^k * (x+y+z-1)! / ((x-1)! * y! * z!)
這是4 * x * 10^k * (x+y+z-1)! / (x! * y! * z!)
4 * x * 10^k * (x+y+z-1)! / (x! * y! * z!)
。
同樣地,5和6貢獻,並且該位置中的數字的總貢獻是: 10^k * (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z)
10^k * (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z)
。
(例如,當x=y=z=1
並且在10 ^ 2位置時,貢獻是400*2 + 500*2 + 600*2 = 3000
(根據示例)。根據計算,它是100 * 2! / (1! * 1! * 1!) * (4+5+6) = 3000
)
(x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z) * (10^0 + 10^1 + ... + 10^(x+y+z-1))
= (x+y+z-1)! / (x! * y! * z!) * (4x + 5y + 6z) * (10^(x+y+z) - 1) / 9
所以在上面的例子中,所有3位數字的總和應該是: 2! / (1! * 1! * 1!) * (4+5+6) * (999)/9 = 3330
2! / (1! * 1! * 1!) * (4+5+6) * (999)/9 = 3330
。 根據示例,它是: 456+465+546+564+645+654 = 3330
。
B部分:
與上面相同,但xy和z分別取0-x,0-y和0-z的值。 這可以通過(0..x),(0..y),(0..z)端點(包括端點)中的3路嵌套循環來完成。 在每次迭代中使用上面的公式
因此,對於上面的示例,我們有x:0-1,y:0-1,z:0-1。 可能的索引是{(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)}
。 根據上述2位數字公式的總和,例如:
(0,1,1):1!/(0!* 1!* 1!)*(5 + 6)* 99/9 = 121(1,0,1):1!/(1!* 0! * 1!)*(4 + 6)* 99/9 = 110(1,1,0):1!/(1!* 1!* 0!)*(4 + 5)* 99/9 = 99
最多可達330
。 在該示例中, 45+54+56+65+46+64 = 330
。
同樣,對於給出15的單位。因此總和是15+330+3330=3675
。
注意:
以上可以推廣到鏈接問題和任意數量的數字(不要求數字是連續的)。 如果數字中有零,則必須略微調整方法,但基本原理是相同的。
您可以使用類似的技術來計算出1到100萬等7的數量。這是一種強大的組合方法。
python 3中的解決方案,它使用具有重復算法的排列。 可以適應其他情況,因為輸入是一個字符,其中鍵是所請求的數字,值是每個數字的計數。
算法解釋:您可以將排列看作樹,其中根包含零長度數,其子代表1位數字,下一級有2位數字等。每個節點有3個子節點,其中表示由數字擴展的父節點的值。 所以該算法基本上是一個預訂樹步行。 每個遞歸調用獲取當前數字,並保留要添加的數字(在字典中維護,數字為鍵,計數為值)。 它依次在字典上依次添加每個可能的數字,然后用新的數字和數字進行遞歸。 該方法還在開頭返回當前數字,然后執行所述遞歸。
#!/usr/bin/env python3
import itertools
import copy
class Matrix:
def __init__(self, dim):
m=None
for i in dim:
m=[copy.deepcopy(m) for j in range(i)]
self.mat=m
def getVal(self, coord):
m=self.mat
for i in coord:
m=m[i]
return m
def setVal(self, coord, val):
m=self.mat
l=coord.pop()
for i in coord:
m=m[i]
coord.append(l)
m[l]=val
def sumOfNumbers(digits):
def _sumOfNumbers(counts):
max_v=-1
for v in counts:
if v<0:
return (0,0)
elif v>max_v:
max_v=v
if m.getVal(counts)==None:
c=0
s=0
if max_v==0:
c=1
else:
for i, d in enumerate(digits.keys()):
counts[i]-=1
r=_sumOfNumbers(counts)
counts[i]+=1
c+=r[0]
s+=r[1]*10+r[0]*d
m.setVal(counts, (c,s))
return m.getVal(counts)
dim=[v+1 for v in digits.values()]
m=Matrix(dim)
tot_val=0
for i in itertools.product(*map(lambda x: range(x), dim)):
r=_sumOfNumbers(list(i))
tot_val+=r[1]
return tot_val
def main():
x=1
y=1
z=1
print(x,y,z)
print(sumOfNumbers({4: x, 5: y, 6: z}))
if __name__ == "__main__":
main()
這就是你需要的! 希望它正常工作:)
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int main(){
int q, a=0, b=0, c=0, x, y, z, l, r,count=0;
long long int sum = 0,i,n,temp;
cin >> x >> y>>z;
string xyz = "4";
for (i = 0; i>-1; i++)
{
n = i;
//sum = 12345620223994828225;
//cout << sum;
while (n > 0)
{
temp = n % 10;
if
(temp == 4)
{
a++;
}
if (temp == 5)
{
b++;
}
if (temp == 6)
{
c++;
}
count++;
n = n / 10;
}
if (a <= x && b <= y && c <= z && (a + b + c) == count)
{
temp = i%mod;
sum = (sum + temp) % mod;
}
else if ((a + b + c) > (x + y + z))
break;
if (count == c)
{
i = 4 * pow(10, c);
}
count = 0;
a = 0;
b = 0;
c = 0;
temp = 0;
}
cout << sum+4;
return 0;
}
只計算4,5和6的出現次數,並使用memoization將其存儲在第二個變量中。下面是C ++代碼
#include <bits/stdc++.h>
#define ll int
#define mod 1000000007
using namespace std;
struct p
{
ll f,s;
}dp[102][102][102]={0};
p c(ll x,ll y,ll z)
{
if (min(x,min(y,z)) < 0)
{
p temp;
temp.f=temp.s=0;
return temp;
}
if (!max(x,max(y,z)))
{
p temp;
temp.f=1;
temp.s=0;
return temp;
}
if(dp[x][y][z].f&&dp[x][y][z].s) return dp[x][y][z];
p ans;
ans.f=ans.s=0;
for (int i=4;i<7;i++)
{
p temp;
if(i==4) temp=c(x-1, y, z);
if(i==5) temp=c(x, y-1, z);
if(i==6) temp=c(x, y, z-1);
ans.f = (ans.f+temp.f)%mod;
ans.s = ((long long)ans.s+((long long)i)*(long long)(temp.f) + 10*(long long)temp.s)%mod;
}
dp[x][y][z].f=ans.f;
dp[x][y][z].s=ans.s;
return ans;
}
int main()
{
ll x,y,z,ans=0;
scanf("%d%d%d",&x,&y,&z);
for (ll i = 0; i <= x; ++i)
{
for (ll j = 0; j <= y; ++j)
{
for (ll k = 0; k <= z; ++k)
{
ans = (ans + c(i, j, k).s)%mod;
cout<<dp[i][j][k].f<<" "<<dp[i][j][k].s<<endl;
}
}
}
printf("%d",ans);
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.