1811 E Living Sequence 两种解法

Posted Edwin_Aze

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1811 E Living Sequence 两种解法相关的知识,希望对你有一定的参考价值。

1811E 1811 E Living Sequence 两种解法 数位DP 思维 进制转换 数位DP 无前导0 九进制

思维 进制转换 数位DP 无前导0 T3
Problem - 1811E - Codeforces

题目大意

从一个不含有数字4的递增序列中找第k个数并输出。
\\(1,2,3,5,6,7,8,9,10,11,12\\), \\(k = 4\\) 时输出 \\(5\\)

思路1

有一个巧妙的解法:
考虑这个问题, 从一个没有限制的从1开始的递增序列找出第k个数, 显然就是十进制的k。而这里则可以定义新的进制为 "012356789" 9进制, 那么k对应的就是这个特殊的九进制数, 我们只需要把它转换为十进制就行。

二转十:

while(k)
	ans += k % 2, k /= 2;

九转十:

while(k)
	ans += k % 9, k /= 9;

代码1

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
using LL = long long;
int a[20];
int cnt = 0;

int main()


    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    string s = "012356789";
    int T;
    cin >> T;
    while (T--)
    

        LL k;
        cin >> k;
        cnt = 0;
        while (k)
            a[cnt++] = s[k % 9] - \'0\', k /= 9;
        for (int i = cnt - 1; i >= 0; i--)
            cout << a[i];
        cout << endl;
    

思路2

也可以考虑数位DP, 定义 \\(f(i,j)\\) 为长度为i, 且最高位为j的数, 可以写出这样的初始化函数来得到 \\([1,i]\\) 的满足条件的数的个数:

void init()

    for (int i = 0; i <= 9; i++)
        if (i != 4)
            f[1][i] = 1;
    for (int i = 2; i <= N - 1; i++)
    
        for (int j = 0; j <= 9; j++)
        
            if (j == 4)
                continue;
            for (int k = 0; k <= 9; k++)
                f[i][j] += f[i - 1][k];
        
    

然后再实现查找前缀和 \\([1,num]\\) 的满足条件的数的个数, 题目中的 \\(k\\) 最大为 1e12, 直接二分结果, 找最左边且 \\(dp(mid) = k\\) 的值就是最终结果。

记得要处理前导0, 方法是在首尾不加上0开头的部分, 最后再加一遍所有长度小于 num.size() 的部分。

代码2

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <vector>
using namespace std;
const int N = 17;
typedef long long ll;
const ll INF = 1e17;
ll f[N][10];

void init()

    for (int i = 0; i <= 9; i++)
        if (i != 4)
            f[1][i] = 1;
    for (int i = 2; i <= N - 1; i++)
    
        for (int j = 0; j <= 9; j++)
        
            if (j == 4)
                continue;
            for (int k = 0; k <= 9; k++)
                f[i][j] += f[i - 1][k];
        
    


ll dp(ll x)

    if (!x)
        return 0;

    vector<int> nums;
    while (x)
        nums.push_back(x % 10), x /= 10;

    ll res = 0;
    for (int i = nums.size() - 1; i >= 0; i--)
    
        int x = nums[i];
        for (int j = (i == nums.size() - 1); j < x; j++)
            res += f[i + 1][j];
        if (x == 4)
            break;
        if (!i)
            res++;
    
    for (int i = 1; i <= nums.size() - 1; i++)
        for (int j = 1; j <= 9; j++)
            res += f[i][j];
    return res;


int main()

    init();

    int T;
    cin >> T;
    while (T--)
    
        ll k;
        cin >> k;
        ll l = -1, r = 1e13;
        while (l != r - 1)
        
            ll mid = l + r >> 1;
            if (dp(mid) < k)
                l = mid;
            else
                r = mid;
        
        cout << r << endl;
    
    return 0;

E - Number Sequence

Description

A single positive integer i is given. Write a program to find the digit located in the position i in the sequence of number groups S1S2...Sk. Each group Sk consists of a sequence of positive integer numbers ranging from 1 to k, written one after another.         For example, the first 80 digits of the sequence are as follows:         11212312341234512345612345671234567812345678912345678910123456789101112345678910

Input

The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by one line for each test case. The line for a test case contains the single integer i (1 ≤ i ≤ 2147483647)      

Output

There should be one output line per test case containing the digit located in the position i.      

Sample Input

2
8
3

Sample Output

2
2


这道题不打表会超时 ,以下 超时代码
#include<iostream>
#include<cmath>
using namespace std;
void f(int n)
{
    int p=1,j=1,t;
    bool flag;
    while(1){
        flag=false;
        for(j=0;j<=p;j++){
            int i;
            for(i=0;;i++){
                t=pow((double)10,i);
                if(j/t==0){
                    n-=i;
                    break;
                }
            }
            if(n<=0){
                int k=i+n;
                flag=true;
                n=j%(int)(pow((double)10,i-k+1))/pow((double)10,i-k);
                break;
            }
        }
        if(flag==true)break;
        p++;
    }
    cout<<n<<endl;
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        f(n);
    }
    //system("pause");
    return 0;
}

 

打表后!!!这个代码不好理解

 

#include<iostream>
#include<cmath>
using namespace std;
unsigned int a[31270],s[31270];
void f()
{
    int i;
    a[1]=1;
    s[1]=1;
    for(i=2;i<31270;i++)
    {
        a[i]=a[i-1]+(int)log10((double)i)+1;   //记录1至s[i]个数字的位数和
        s[i]=s[i-1]+a[i];                      //一位 记录 1至s[i]个数字
    }    
}    
int main()
{
    int t;
    int n;
    int i;
    cin>>t;
    f();
    while(t--)
    {
        cin>>n;
        i=1;
        while(s[i]<n) i++;
        int pos=n-s[i-1]; 
        int tmp=0;
        for(i=1;tmp<pos;i++)           //第n个数字在s[i-1]这个数据组中
        {
            tmp+=(int)log10((double)i)+1;
        }   
        int k=tmp-pos;       //数字i从低位数的第k+1位
        cout<<(i-1)/(int)pow(10.0,k)%10<<endl;
    }  
    return 0; 
}

 

以上是关于1811 E Living Sequence 两种解法的主要内容,如果未能解决你的问题,请参考以下文章

[洛谷P3929]SAC E#1 - 一道神题 Sequence1

论文阅读Research on video adversarial attack with long living cycle

E - Number Sequence

[codeforces]#350E. Correct Bracket Sequence Editor

Kotlin 的 Iterable 和 Sequence 看起来完全一样。为啥需要两种类型?

Get busy todie or get busy living?