1. 程式人生 > 實用技巧 >P1066 2^k進位制數

P1066 2^k進位制數

P1066 2^k進位制數

題目連結

題目描述

設 r 是個 2^k進位制數,並滿足以下條件:

  • r 至少是個 2 位的 2^k進位制數。
  • 作為 2^k進位制數,除最後一位外,r的每一位嚴格小於它右邊相鄰的那一位。
  • 將 r轉換為二進位制數 q後,則 q的總位數不超過 w。

在這裡,正整數 k,w 是事先給定的。

問:滿足上述條件的不同的 r共有多少個?

我們再從另一角度作些解釋:設 S是長度為 w 的 0101 字串(即字串 S 由 w 個 0 或 1 組成),S對應於上述條件三中的 q。將 S從右起劃分為若干個長度為 k的段,每段對應一位 2^k 進位制的數,如果 S至少可分成 2段,則 S所對應的二進位制數又可以轉換為上述的 2^k進位制數 r。

例:設 k=3,w=7 k =3。則 r 是個八進位制數( 2^3=8 )。由於 w=7,長度為 7 的 01 字串按 33位一段分,可分為 3 段(即 1,3,3,左邊第一段只有一個二進位制位),則滿足條件的八進位制數有:

2 位數:
高位為 1:6 個(即 12,13,14,15,16,17 ),
高位為 2:5 個,
…,
高位為 6:1個(即 67 )。
共 6+5+…+1=21 個。

3位數:
高位只能是 1,
第 2位為 2:5 個(即 123,124,125,126,127),
第 2 位為 3:4 個,
…,
第 2位為 6:1個(即 167)。
共 5+4+…+1=15 個。

所以,滿足要求的 r共有 36 個。

輸入格式

一行兩個正整數 k,w 用一個空格隔開:

輸出格式

一行一個個正整數,為所求的計算結果。
即滿足條件的不同的 r的個數(用十進位制數表示),要求不得有前導零,各數字之間不得插入數字以外的其他字元(例如空格、換行符、逗號等)。

(提示:作為結果的正整數可能很大,但不會超過 200 位)

解析

首先我們來理解題意,r為k進位制數,轉化為二進位制後,01串最多位不能超過w。由於k位01串可以組成一個k進位制數,我們不妨將最多位可能的情況下的w按k位一組進行切分,得到L = ceil(w/k)組,那麼r在k進位制情況下最多有L位,最少有2位。

例如輸入3,7時,r位8進位制數,r的每位由3個二進位制位構成,r最多情況下可能由7個二進位制位構成。我們將7按3位一組進行切分,得到[1,3,3]。

r在k進位制下最少只有兩位,最多L位,則r的所有情況為 2位r下的情況到L位r下的情況總數之和。

以 3 7為例,則r可能有2位或3位,當r兩位的時候,可能情況有 12,13,14,..17,23,24..27,67,即在1..7的數字中選擇2個,構成二位的r。當r三位時,即首先選擇出第一位,並在剩下的位中選擇出2個比第一位大的數。

由此,可以看出這個問題實際上是組合數問題。

當前n個分段,每個分段均為k位時,則每段可選擇的數字為2^k-1,而r的可能情況為(2^k-1)中取i位(i從2到n)

最後一個分段有可能不到k位,則首先從1開始選擇,做出第一次選擇後,計算出剩下n-1個分段的組合數,並依次求和,得到最終結果。

接下來問題就轉換到如何計算組合數了。

計算組合數可以參考這篇博文 參考連結

此處使用楊輝三角遞推求解組合數值。

最後本題中會出現大整數,超出 long long int範圍,因此對於C++/C實現來說需要手寫大整數

在此首先給出Java版本程式碼



import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Scanner;

public class Main {
    private static final int n = 1000;
    static BigInteger [][]comb = new BigInteger[1000][1000];
    static void init()
    {
        for(int i=0;i<n ;i++)
        {
            comb[i][0] = BigInteger.valueOf(1);
            comb[i][i] = BigInteger.valueOf(1);
            for(int j=1;j<i;j++)
            {
                comb[i][j] = comb[i-1][j].add(comb[i-1][j-1]);
            }
        }

    }


    static BigInteger C(int n, int m)
    {
        if(n<m) return BigInteger.valueOf(0);
        return comb[n][m];
    }

    public static void main(String[] args) {
        BigInteger cnt =BigInteger.valueOf(0);
        init();
        int k,w;
        Scanner in = new Scanner(System.in);
        k = in.nextInt();
        w = in.nextInt();
        int fullPart = w / k;
        int divPart = (int) Math.ceil(1.0*w / k);
        int resPart = 0;
        if (w % k != 0)
        {
            resPart = (int) (w - k * fullPart);
        }
        int sel = (int) (Math.pow(2, k) - 1);
        for (int i = 2; i <= divPart; i++)
        {
            if (i != divPart)
            {
                cnt = cnt.add(C(sel,i));
            }
            else
            {
                if (resPart != 0)
                {
                    int t = (int) (Math.pow(2, resPart) - 1);
                    for (int j = 1; j <= t; j++)
                    {
                        cnt = cnt.add(C(sel-j,i-1));
                    }
                }
                else
                {
                    cnt = cnt.add(C(sel,i));
                }
            }

        }
        System.out.println(cnt);
    }
}

C語言程式碼 此處大整數程式碼來自於其他博文,一時找不到出處

#include<stdio.h>
#include<string.h>
#include<math.h>
#define ll long long int
#define maxChar 130
#define maxlen 260
typedef struct HP
{int len;
char s[maxChar];
}HP;
void PrintHP(HP x) {for(int i=x.len;i>=1;i--) printf("%d",x.s[i]);}
void Str2HP(const char *s,HP *x)
{
	x->len=strlen(s);
	for(int i=1;i<=x->len;i++) x->s[i]=s[x->len-i]-'0'; //倒著記錄
}
void Plus(const HP a,const HP b,HP*c)
{
	int i;c->s[1]=0;
	for(i=1;i<=a.len||i<=b.len||c->s[i];i++){
		if(i<=a.len) c->s[i]+=a.s[i];
		if(i<=b.len) c->s[i]+=b.s[i];
		c->s[i+1]=c->s[i]/10;c->s[i]%=10;
	}
	c->len=i-1;if(c->len==0) c->len=1;
}
HP comb[maxlen][maxlen];
void init()
{
        for(int i=0;i<maxlen ;i++)
        {
            
            Str2HP("1",&comb[i][0]);
            Str2HP("1",&comb[i][i]);
            for(int j=1;j<i;j++)
            {
                Plus(comb[i-1][j],comb[i-1][j-1],&comb[i][j]);
            }
        }
}
HP C(ll n, ll m)
{
    HP t;
    Str2HP("0",&t);
    if(n<m) return t;
    return comb[n][m];
}

int main()
{
    HP ans;
    HP temp;
    ll cnt = 0;
    Str2HP("0",&ans);
    ll k, w;
    init();
    scanf("%lld %lld", &k, &w);
    ll fullPart = w / k;
    ll divPart = ceil(1.0*w / k);
    int resPart = 0;
    if (w % k != 0)
        resPart = w - k * fullPart;
    int sel = pow(2, k) - 1;
    for (int i = 2; i <= divPart; i++)
    {
        if (i != divPart)
        {
            HP t=  C(sel,i);
            HP a = ans;
            Plus(a,t,&ans);
        }
        else
        {
            if (resPart != 0)
            {
                int t = pow(2, resPart) - 1;
                for (int j = 1; j <= t; j++)
                {
                    HP x = C(sel - j, (i - 1));
                    HP a = ans;
                    Plus(a,x,&ans);
                }
            }
            else
            {
                HP x = C(sel, i);
                HP a = ans;
                Plus(a,x,&ans);
            }

        }

    }
    PrintHP(ans);
}