ได้รับจำนวนเต็ม N. อะไรคือจำนวนเต็มเล็กที่สุดที่มากกว่า N ที่มีตัวเลขเป็น 0 หรือ 1 เท่านั้น


15

ฉันมีจำนวนเต็ม N. ฉันต้องไปหามากขึ้นเลขที่เล็กที่สุดกว่าไม่มีข้อความที่ไม่ประกอบด้วยหลักอื่น ๆ ที่ไม่ใช่ 0 หรือ 1 ตัวอย่างเช่นหากแล้วคำตอบคือN = 12 100ฉันเขียนรหัสวิธีบังคับเดรัจฉานใน C ++

int main() {
    long long n;
    cin >> n;

    for (long long i = n + 1; ; i++) {
        long long temp = i;
        bool ok = true;
        while (temp != 0) {
            if ( (temp % 10) != 0 && (temp % 10) != 1) {
                ok = false;
                break;
            }
            temp /= 10;
        }
        if (ok == true) {
            cout << i << endl;
            break;
        }
    }
}

ปัญหาคือวิธีการของฉันช้าเกินไป ฉันเชื่อว่ามีวิธีที่มีประสิทธิภาพมากในการแก้ปัญหานี้ ฉันจะแก้ปัญหานี้อย่างมีประสิทธิภาพได้อย่างไร


4
เริ่มต้นด้วยหน่วย หากตัวเลขอื่นที่ไม่ใช่ 0 หรือ 1 ให้วางศูนย์แล้วถือ 1 ทำซ้ำสำหรับแต่ละตำแหน่ง
Sembei Norimaki

1
สิ่งนี้อธิบายถึงปัญหาที่คล้ายกัน อาจช่วย
TomBombadil

Nอนุญาตให้ติดลบหรือไม่ นอกจากนี้ยังเป็นเรื่องยากเนื่องจากคุณมีความเสี่ยงที่จะได้รับข้อมูลมากเกินไป ขอบเขตมีNอะไรบ้าง?
Bathsheba

1
@SembeiNorimaki: นี่มันผิด มันจะไม่เปลี่ยนแปลงจำนวนที่ทำจาก 0 และ 1 โดยเฉพาะและมีความล้มเหลวอื่น ๆ
Yves Daoust

1
@SembeiNorimaki: ฉันบอกว่ามีความล้มเหลวอื่น ๆ มันยังคงอยู่เนื่องจากวิธีการของคุณผิด ลองจำนวนเต็มตั้งแต่ 1 ถึง 50 แล้วคุณจะพบข้อผิดพลาด ผิดพลาด humanum, perseverare diabolicum
Yves Daoust

คำตอบ:


20
  1. เพิ่ม N

  2. เริ่มต้นจากด้านซ้ายสแกนจนกว่าคุณจะพบตัวเลขด้านบน 1 เพิ่มจำนวนบางส่วนก่อนที่จะเพิ่มขึ้นและศูนย์ส่วนที่เหลือ

เช่น

12 -> 13 -> 1|3 -> 10|0
101 -> 102 -> 10|2 -> 11|0
109 -> 110 -> 110|
111 -> 112 -> 11|2 -> 100|0
198 -> 199 -> 1|99 -> 10|00
1098 -> 1099 -> 10|99 -> 11|00
10203 -> 10204 -> 10|204 -> 11|000
111234 -> 111235 -> 111|235 -> 1000|000
...

พิสูจน์:

จำนวนที่ร้องขอต้องมีอย่างน้อย N + 1 นี่คือสาเหตุที่เราเพิ่มขึ้น ตอนนี้เรากำลังมองหาตัวเลขที่มากกว่าหรือเท่ากัน

ให้เราเรียกคำนำหน้าว่า 0/1 หลักเริ่มต้นและต่อท้ายสิ่งที่เกิดขึ้นหลังจากนั้น เราต้องแทนที่ตัวเลขแรกของคำต่อท้ายด้วยศูนย์และตั้งค่าคำนำหน้าให้ใหญ่ขึ้น คำนำหน้าเล็กที่สุดที่เหมาะสมคือคำนำหน้าปัจจุบันบวกหนึ่ง และส่วนต่อท้ายที่เล็กที่สุดที่พอดีคือศูนย์ทั้งหมด


ปรับปรุง:

ฉันลืมระบุว่าคำนำหน้าต้องเพิ่มเป็นเลขฐานสองมิฉะนั้นตัวเลขต้องห้ามอาจปรากฏขึ้น


7

ความเป็นไปได้อีกอย่างหนึ่งคือสิ่งต่อไปนี้:

  • คุณเริ่มต้นด้วยตัวเลขทศนิยมที่ใหญ่ที่สุดของชนิด "1111111 ... 1111" ที่สนับสนุนโดยชนิดข้อมูลที่ใช้

    อัลกอริทึมสันนิษฐานว่าอินพุตมีค่าน้อยกว่าจำนวนนี้ มิฉะนั้นคุณจะต้องใช้ประเภทข้อมูลอื่น

    ตัวอย่าง: เมื่อใช้คุณจะเริ่มต้นด้วยหมายเลขlong long1111111111111111111

  • จากนั้นประมวลผลแต่ละหลักทศนิยมจากซ้ายไปขวา:
    • ลองเปลี่ยนตัวเลขจาก 1 เป็น 0
    • หากผลลัพธ์ยังคงมีขนาดใหญ่กว่าอินพุตของคุณให้ทำการเปลี่ยนแปลง (เปลี่ยนตัวเลขเป็น 0)
    • มิฉะนั้นตัวเลขยังคงอยู่ 1

ตัวอย่าง

Input = 10103
Start:  111111
Step 1: [1]11111, try [0]11111; 011111 > 10103 => 011111 
Step 2: 0[1]1111, try 0[0]1111; 001111 < 10103 => 011111
Step 3: 01[1]111, try 01[0]111; 010111 > 10103 => 010111
Step 4: 010[1]11, try 010[0]11; 010011 < 10103 => 010111
Step 5: 0101[1]1, try 0101[0]1; 010101 < 10103 => 010111
Step 6: 01011[1], try 01011[0]; 010110 > 10103 => 010110
Result: 010110

หลักฐานความถูกต้อง:

เราประมวลผลหลักทีละหลักในอัลกอริทึมนี้ ในแต่ละขั้นตอนจะมีตัวเลขที่ทราบค่าแล้วและตัวเลขที่ยังไม่ทราบค่า

ในแต่ละขั้นตอนเราจะตรวจสอบตัวเลขที่ไม่รู้จักซ้ายสุด

เราตั้งค่าตัวเลขนั้นเป็น "0" และตัวเลขที่ไม่รู้จักอื่น ๆ ทั้งหมดเป็น "1" เนื่องจากตัวเลขที่จะตรวจสอบมีความสำคัญที่สุดของตัวเลขที่ไม่รู้จักตัวเลขที่ได้คือจำนวนที่มากที่สุดที่เป็นไปได้โดยที่ตัวเลขนั้นเป็น "0" หากจำนวนนี้น้อยกว่าหรือเท่ากับอินพุตตัวเลขที่ถูกโพรบจะต้องเป็น "1"

ในทางกลับกันจำนวนผลลัพธ์จะน้อยกว่าตัวเลขที่เป็นไปได้ทั้งหมดซึ่งตัวเลขที่ถูกตรวจสอบคือ "1" หากตัวเลขผลลัพธ์มีค่ามากกว่าอินพุตตัวเลขต้องเป็น "0"

ซึ่งหมายความว่าเราสามารถคำนวณหนึ่งหลักในแต่ละขั้นตอน

รหัส C

(รหัส C ควรทำงานภายใต้ C ++ เช่นกัน):

long long input;
long long result;
long long digit;

... read in input ...

result = 1111111111111111111ll;
digit = 1000000000000000000ll;

while( digit > 0 )
{
    if(result - digit > input)
    {
        result -= digit;
    }
    digit /= 10;
}

... print out output ...

3

ฉันขอแนะนำทางเลือกสองทาง

I. การเพิ่ม พิจารณาว่าเป็นการดัดแปลงของ @YvesDaoust วิธี

  1. เพิ่ม N 1
  2. ขยายผลลัพธ์ด้วยศูนย์นำหน้า
  3. เริ่มจากหลักสุดท้ายถึงหลักสอง
    (a) ถ้ามันน้อยกว่า 2 แล้วปล่อยให้ทุกอย่างเป็น
    (b) มิฉะนั้นตั้งเป็น 0 และเพิ่มก่อนหน้า
  4. ทำซ้ำขั้นตอนที่ 3a, b

ตัวอย่าง:

1. N = 0 -> 1 -> (0)|(1) -> 1
2. N = 1 -> 2 -> (0)|(2) -> (1)|(0) -> 10
3. N = 101 -> 102 -> (0)|(1)(0)(2) -> (0)|(1)(1)(0) -> (0)|(1)(1)(0) -> (0)|(1)(1)(0) -> 110
4. N = 298 -> 299 -> (0)|(2)(9)(9) -> (0)|(2)(10)(0) -> (0)|(3)(0)(0) -> (1)|(0)(0)(0) -> 1000

คุณได้ผลลัพธ์เป็นรูปแบบทศนิยม


ครั้งที่สอง หาร

  1. เพิ่ม N 1
  2. กำหนดผลรวมเป็น 0
  3. หารผลลัพธ์ด้วย 10 เพื่อรับส่วน div (D) และ mod (M)
  4. ตรวจสอบ M
    (a) ถ้า M เกิน 1 จากนั้นเพิ่ม D
    (b) มิฉะนั้นเพิ่มผลรวมด้วย M * 10 kโดยที่ k คือหมายเลขการวนซ้ำปัจจุบัน (เริ่มต้นด้วย 0)
  5. ทำซ้ำขั้นตอนที่ 3,4 จนกระทั่ง D เป็น 0

ตัวอย่างที่ 1:

1. N = 0 -> N = 1
2. sum = 0
3. 1/10 -> D == 0, M == 1 -> sum = sum + 1*10^0 == 1
4. D == 0 -> sum == 1

ตัวอย่างที่ 2:

1. N = 1 -> N = 2
2. sum = 0
3. 2/10 -> D == 0, M == 2 -> D = D + 1 == 1
4. 1/10 -> D == 0, M == 1 -> sum = sum + 1*10^1 == 10
5. D == 0, sum == 10

ตัวอย่างที่ 3:

1. N = 101 -> N = 102
2. sum = 0
3. 102/10 -> D == 10, M == 2 -> D = D + 1 == 11
4. 11/10 -> D == 1, M == 1 -> sum = sum + 1*10^1 = 10
5. 1/10 -> D == 0, M == 1 -> sum = sum + 1*10^2 == 10 + 100 == 110
6. D == 0, sum == 110

ตัวอย่างที่ 4:

1. N = 298 -> N = 299
2. sum = 0
3. 299/10 -> D == 29, M == 9 -> D = D + 1 == 30
4. 30/10 -> D == 3, M == 0 -> sum = sum + 0*10^1 == 0
5. 3/10 -> D == 0, M == 3 -> D = D + 1
6. 1/10 -> D == 0, M == 1 -> sum = sum + 1*10^3 == 1000
7. D == 0, sum == 1000
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.