U389139 至少有一位重複的數字-題解

StevenJiahao發表於2024-09-22

題目傳送門

一、舉例說明

\(654923\) 為例
要判斷在 \([0, 654923]\) 區間至少有一位重複的數值的數,可以考慮其補集,即\(\color{red}所有位數均不重複的數\),用 \(N\) 減去補集即為結果。
首先可以將其分為兩種情況。

  • 情況一,位數小於 \(6\) 位。所有位數均不重複的有 \(9 \times 9 \times 8 \times 7 \times 6\) 種(首位不為0)。
  • 情況二,位數等於 \(6\) 位,這也可以分為兩種情況,首位小於 \(6\) 或等於 \(6\) 。對於首位小於 \(6\) 的資料,所有位數均不重複的有 \(5 \times 9 \times 8 \times 7 \times 6 \times 5\) 種情況。

對於首位等於 \(6\) ,那麼就要考慮次高位。可以定義一個 \(st\) 陣列,來記錄是否有重複的數,比如 \(654423\) ,列舉到第四位也是 \(4\) ,那麼後兩位無論是多少數字都無意義了,都一定沒有不重複的數。因此用該陣列判斷某一個數字是否已被使用,已被使用就直接跳過。
從第二位 \(j=5\) 開始列舉,於是我們第二位考慮比 \(j\) 小的數即 \(0\)\(4\) ,加上後面四位不重複的數,即 \(8 \times 7 \times 6 \times 5 = P(8,4) = P(10-2,4)\),列舉後 \(st[5] = true\)
從第三位 \(j=4\) 開始列舉,於是我們第三位考慮比 \(j\) 小的數即 \(0\)\(3\) ,對於每一位數如果 \(st[k]\) 不為 \(true\) 的話,就說明此時這個數可以來當第三位數,加上後三位不重複的數即 \(7 \times 6 \times 5 = P(7,3) = P(10-3,3)\) ,列舉後 \(st[4] = true\)
以此將每一位數進行迴圈判定。每次迴圈完畢之後都判斷一次,對應位數 \(j\) 是否重複有 \(st[j] = true\) ,如果重複了就不用繼續往下面迴圈了,後面對應的數一定都重複。
最後跳出迴圈,同時我們在列舉的過程中,可以發現,第二位我們沒有列舉 \(5\) ,第二位沒有列舉 \(4\) …,第六位沒有列舉 \(3\) ,如果能夠列舉到這裡,說明列舉過程中沒有遇到重複的數,即這個數不是重複的數,所以要減去這個數。

二、程式碼實現

#include <bits/stdc++.h>
using namespace std;
int getSum(int a,int b){
    int res = 1;
    for(int i = a;b>0;b--,a--){
        res *= a;
    }
    return res;
}
vector<int>nums;
int numDupDigitsAtMostN(int n) {
    int res = n;
    //先將數整理為陣列
    while(n) {
        nums.push_back(n % 10);
        n /= 10;
    }
    //情況一 比給定數位數少
    for(int i = nums.size() - 1;i > 0;i--){
        res -= 9 * getSum(9,i-1);
    }
    //情況二 和給定數位數相同 但最高位小於給定數最高位
    res -= (nums.back() - 1) * getSum(9,nums.size() - 1);
    //情況三 和給定數位數相同 最高位相同
    vector<bool> st(10);
    //然後去除首位的情況,設定首位為true不可再使用
    st[nums.back()] = true;
    for(int i = nums.size() - 2;i >= 0;i--){
        int j = nums[i];
        for(int k = 0;k < j;k++){
            //每次遇到前面用過的數就continue
            if(st[k]) continue;
            res -= getSum(10 - (nums.size() - i),i);
        }
        if(st[j]) {
            return res;
        }
        st[j] = true;
    }
    return res-1;
}
int main()
{
    int originNum;
    cin>>originNum ;
    cout<<numDupDigitsAtMostN(originNum);
    return 0;
}

如果你看到這裡,你就浪費了2分鐘,因為這程式碼太煩了(bushi

相關文章