時間限制:(每個case)2s 空間限制:128MB
小Q十分富有,擁有非常多的硬幣,小Q擁有的硬幣是有規律的,對於所有的非負整數K,小Q恰好各有兩個面值爲2^K的硬幣,所以小Q擁有的硬幣就是1,1,2,2,4,4,8,8,…。小Q有一天去商店購買東西需要支付n元錢,小Q想知道有多少種方案從他擁有的硬幣中選取一些拼湊起來恰好是n元(如果兩種方案某個面值的硬幣選取的個數不一樣就考慮爲不一樣的方案)。
輸入:
輸入包括一個整數n(1<=n<=10^18),表示小Q需要支付多少錢。注意n的範圍。
輸出:
輸出一個整數,表示小Q可以拼湊出n元錢放的方案數。
【請注意:javascrip語言不支持調試,請同學們優先考慮使用其他語言,謝謝】
樣例輸入:6
樣例輸出:3
數位dp,將n表示成二進制,從爲1的位置向後轉移。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=200+10;
int dp[maxn];
int nxt[maxn],pos[maxn];
int inx,res;
int dfs(int x){
if(x==-1)return 1;
if(dp[x]!=0)return dp[x];
dp[x]+=dfs(nxt[x]);
for(int i=1;i<x;i++){
if(pos[i]==1)continue;
dp[x]+=dfs(nxt[i]);
}
return dp[x];
}
signed main(){
int n;
scanf("%lld",&n);
res=-1;
while(n){
pos[++inx]=n%2;
nxt[inx]=res;
if(n&1)res=inx;
n/=2;
}
printf("%lld\n",dfs(inx));
return 0;
}
在別人博客中看見了更加簡單的方法,用位運算+分治,將0看成是用了0次或者2次,1表示自己用了一次或者後面用了兩次。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=200+10;
int solve(int n){
if(n==0)return 0;
if(n==1)return 1;
if(n==2)return 2;
if(n&1){
return solve(n>>1);
}
return solve(n>>1)+solve((n-2)>>1);
}
signed main(){
int n;
scanf("%lld",&n);
printf("%lld\n",solve(n));
return 0;
}