『ACM C++』 Codeforces | 1005D - Polycarp and Div 3

来源:https://www.cnblogs.com/winniy/archive/2019/03/04/10474232.html
-Advertisement-
Play Games

今天佛了,魔鬼周一,線上教學,有點小累,但還好,今天AC了一道,每日一道,還好達成目標,還以為今天完不成了,最近任務越來越多,如何高效完成該好好思考一下了~最重要的還是學業的複習和預習。 今日興趣新聞: 《流浪地球》中的逃生氣囊球和馬斯克有什麼關係? 鏈接:https://mbd.baidu.com ...


  今天佛了,魔鬼周一,線上教學,有點小累,但還好,今天AC了一道,每日一道,還好達成目標,還以為今天完不成了,最近任務越來越多,如何高效完成該好好思考一下了~最重要的還是學業的複習和預習。

 

 

今日興趣新聞:

《流浪地球》中的逃生氣囊球和馬斯克有什麼關係?

鏈接:https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_8599496962815210358%22%7D&n_type=0&p_from=1

 

 

------------------------------------------------題目----------------------------------------------------------

Polycarp and Div 3

Polycarp likes numbers that are divisible by 3.

He has a huge number ss. Polycarp wants to cut from it the maximum number of numbers that are divisible by 33. To do this, he makes an arbitrary number of vertical cuts between pairs of adjacent digits. As a result, after mm such cuts, there will be m+1m+1 parts in total. Polycarp analyzes each of the obtained numbers and finds the number of those that are divisible by 33.

For example, if the original number is s=3121s=3121, then Polycarp can cut it into three parts with two cuts: 3|1|213|1|21. As a result, he will get two numbers that are divisible by 33.

Polycarp can make an arbitrary number of vertical cuts, where each cut is made between a pair of adjacent digits. The resulting numbers cannot contain extra leading zeroes (that is, the number can begin with 0 if and only if this number is exactly one character '0'). For example, 007, 01 and 00099 are not valid numbers, but 90, 0 and 10001 are valid.

What is the maximum number of numbers divisible by 33 that Polycarp can obtain?

Input

The first line of the input contains a positive integer ss. The number of digits of the number ss is between 11 and 21052⋅105, inclusive. The first (leftmost) digit is not equal to 0.

Output

Print the maximum number of numbers divisible by 33 that Polycarp can get by making vertical cuts in the given number ss.

Examples

input

3121

output

 2

input

 6

output

 1

input

1000000000000000000000000000000000

output

33

input

201920181

output

 4

Note

In the first example, an example set of optimal cuts on the number is 3|1|21.

In the second example, you do not need to make any cuts. The specified number 6 forms one number that is divisible by 33.

In the third example, cuts must be made between each pair of digits. As a result, Polycarp gets one digit 1 and 3333 digits 0. Each of the 3333digits 0 forms a number that is divisible by 33.

In the fourth example, an example set of optimal cuts is 2|0|1|9|201|81. The numbers 00, 99, 201201 and 8181 are divisible by 33.

------------------------------------------------題目----------------------------------------------------------

 

(一) 原題大意:

  輸入一個數,某人可以進行以下的操作:  

  某人可以進行任意數量的垂直切割,其中每個切割在一對相鄰的數字之間進行。結果數字不能包含額外的前導零(也就是說,當且僅當此數字恰好是一個字元' 0 '時,數字才能以0開頭)。例如,0070100099不是有效的數字,但90010001是有效的。

  分割出來的每一部分的數,都能被3整除,則說明可以得到一個整除3的數,其中0也能被3整除,請算一算能為該同學獲得最多幾個整除3的數呢?

 

(二) 題目分析:

  首先,肯定是需要得到每一個數字,如果這個數字是0,那麼答案比較簡單,如果當前數字能夠直接被3整除(0, 3, 6, 9)的話,那就直接結果加一就好了。 

  然後如果這兩種情況都不成立,那麼就是兩位數以上的組合了,在這裡我出了點彎路子,剛開始用求和法,然後卻瘋狂到test11就WA了,檢查了很久也沒有找到解決辦法,後來心理課結束後和大佬們交談才發現一些新想法奧秘,那就是符合3整除的數,求餘之後有特殊關係,待會列一下我的彎路給自己提個醒。

  然後我換了一種方法,那就是用加和,利用整除3的特性,下麵也列出來了,但還是WA了。

  最後我翻看一些博客,發現在我原來寫得基礎上,只要改變if條件的位置就能AC了,總結來說有三種情況:當前數字模3為0、現有的數字之和模3為0(當前正在處理的)、隔了三個數字了一定可以做到模3為0

 

(三) 錯誤彎路:

  第一次直接用long long int 去扔輸入,然後丟OJ結果發現第一次就WA了

#include<stdio.h>
#include<math.h> 
long long int temp,num;
int temp_num,counter,ans,k;
int main()
{
    ans = 0;
    scanf("%d",&temp);
    num = temp;
    while(num>0)
    {
        k = num % 10;
        if(k == 0)
        {
            ans++;
            counter = temp_num = 0;
            num = num / 10;
            continue;
        }
        if(k % 3 == 0)
        {
            ans++;
            counter = temp_num = 0;
        }
        else
        {
            temp_num += k * pow(10,counter);
            if(temp_num % 3 == 0)
            {
                ans++;
                counter = temp_num = 0;
            }
        }
        num = num / 10;
    }
    printf("%d\n",ans);
    return 0;
 } 

  結果是因為scanf這裡用了%d,結果才發現過樣例1000000000000000000000000000的時候錯了,然後就換了一種方法,像這樣大數都只能用char來處理了:

  第二步彎路,然後就改了改,扔進去:

#include<stdio.h>
#include<math.h> 
#include<string.h>
char input[2000005];
int num;
int temp_num,counter,ans,k;
int main()
{
    ans = 0;
    scanf("%s",&input);
    for(int i = strlen(input) - 1;i>=0;i--)
    {
        k = input[i];
        if(k == 0)
        {
            ans++;
            counter = temp_num = 0;
            continue;
        }
        if(k % 3 == 0)
        {
            ans++;
            counter = temp_num = 0;
        }
        else
        {
            temp_num += k * pow(10,counter);
            if(temp_num % 3 == 0)
            {
                ans++;
                counter = temp_num = 0;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
 } 

  到這裡樣例就都全過了,代碼思路是:將輸入的值扔進char數組裡,然後獲取到他的長度,從後往前來處理,從判斷當前數是否為0,如果是的話就直接加一,然後判斷當前數能否被3整除,如果可以就也直接加一。都沒有的話,就存到一個temp變數里,然後與後面處理的數求和,然後判斷該求和temp是否能被3整除。結果這裡到test11就WA了,半天沒搞懂,後來和同學討論了半天,結果發現對於122 221這樣的數判斷就出現了問題,後來實在解不了,嫌判斷太亂,都單獨列了出來,結果居然AC了:

#include<stdio.h>
#include<math.h> 
#include<string.h>
char input[200005];
long long int temp_num,counter,ans,k;
int main()
{
    ans = counter = 0;
    scanf("%s",&input);
    for(int i = strlen(input) - 1;i>=0;i--)
    {
        k = input[i] - '0';
        temp_num += k;
        counter++;
        if(counter == 3)
        {
            ans++;
            counter = temp_num = 0;
            continue;
        }
        else if(k % 3 == 0)
        {
            ans++;
            counter = temp_num = 0;
            continue;
        }
        else if(temp_num % 3 == 0)
        {
            ans++;
            counter = temp_num = 0;
            continue;
        }
    }
    printf("%d\n",ans);
    return 0;
 } 

  結果誤打誤撞發現了一些牛皮的數學思維~ 待會獻上:

 

(四)AC代碼:

    因為代碼比較簡單,依舊不分塊了~

#include<stdio.h>
#include<math.h> 
#include<string.h>
char input[200005];
long long int temp_num,counter,ans,k;
int main()
{
    ans = counter = 0;
    scanf("%s",&input);
    for(int i = strlen(input) - 1;i>=0;i--)
    {
        k = input[i] - '0';
        temp_num += k;
        counter++;
        if(counter == 3 || k % 3 == 0 || temp_num % 3 == 0)
        {
            ans++;
            counter = temp_num = 0;
        }
    }
    printf("%d\n",ans);
    return 0;
 } 

 

 

 

(五)AC截圖:

 

 

(六)解後分析:

    分析1 - 能被特殊數整除的特征

1、能被2整除的數的特征。

   如果一個數能被2整除,那麼這個數末尾上的數為偶數,“0”、“2”、“4”、“6”、“8”。

2、能被3整除的數的特征。

   如果一個數能被3整除,那麼這個數所有數位上數字的和是3的倍數。例如:225能被3整除,因為2+2+5=9,9是3的倍數,所以225能被3整除。

3、能被4整除的數的特征。

   如果一個數的末尾兩位能被4整除,這個數就能被4整除。例如:15692512能不能被4整除呢?因為15692512的末尾兩位12,能被4整除,所以15692512能被4整除。

4、能被5整除的數的特征。

   若一個數的末尾是0或5,則這個數能被5整除。

5、7整除的數的特征。

方法一:若一個整數的個位數字截去,再從餘下的數中,減去個位數的2倍,如果差是7的倍數,則原數能被7整除。如果差太大或心算不易看出是否是7的倍數,就需要繼續上述「截尾、倍大、相減、驗差」的過程,直到能清楚判斷為止。例如,判斷133是否是7的倍數的過程如下:13-3×2=7,所以133是7的倍數;又例如判斷6139是否7的倍數的過程如下:613-9×2=595 , 59-5×2=49,所以6139是7的倍數,以此類推。

方法二:如果一個多位數的末三位數與末三位以前的數字所組成的數的差,是7的倍數,那麼這個數就能被7整除。例如:280678末三位數是678,末三位以前數字所組成的數是280,679-280=399,399能被7整除,因此280679也能被7整除。

方法三:首位縮小法,減少7的倍數。

例如,判斷452669能不能被7整除,452669-420000=32669,只要32669能被7整除即可。可對32669繼續,32669-28000=4669,4669-4200=469,469-420=49,49當然被7整除所以452669能被7整除。
6、能8整除的數的特征。
   若一個整數的未尾三位數能被8整除,則這個數能被8整除。
7、能9整除的數的特征。
   若一個數的數位上的數字的和能被9整除,則這個整數能被9整除。例如:111111111能不能被9整除呢?因為1+1+1+1+1+1+1+1+1=9,9是9的倍數,所以111111111能被9整除。
8、能11整除的數的特征。
   方法一:若一個整數的奇位數字之和與偶位數字之和(從右往左數)的差能被11整除,則這個數能被11整除。例如,判斷491678能不能被11整除。奇位數字之和8+6+9=23;偶位數字之和7+1+4=12;23-12=11,11能被11整除,所以491678能被11整除。這種方法叫作“奇偶位差法”。  

方法二:11的倍數檢驗法也可用上述檢查7的「割尾法」處理!過程唯一不同的是:倍數不是2而是1!例如:判斷491678能不能被11整除,49167-8=49159,4915-9=4906,

490-6=484,48-4=44。44能被11整除,所以得491678能被11整除。

方法三:還可以根據7的方法二判斷。例如:283679的末三位數是679,末三位以前數所組成的數是283,679-283=396,396能被11整除,因此283679就一定能被11整除。
9、能13整除的數的特征。
   方法一:若一個整數的個位數字截去,再從餘下的數中,加上個位數的4倍,如果和13的倍數,則原數能被13整除。如果和太大或心算不易看出是否13的倍數,就需要繼續上述「截尾、倍大、相加、驗和」的過程,直到能清楚判斷為止。

例如,判斷1284322能不能被13整除。128432+2×4=128440,12844+0×4=12844,

1284+4×4=1300,1300÷13=100。所以1284322能被13整除。

方法二:前面7的方法二,也適用判定13。例如:判定1284322能不能被13整除,128432的末尾三位數是322,末尾以前的數字所組成的數是1284,322-1284=-962。962÷13=74。所以1284322能被13整除。
10、能17整除的數的特征。
    方法一:若一個整數的個位數字截去,再從餘下的數中,減去個位數的5倍,如果差是17的倍數,則原數能被17整除。如果差太大或心算不易看出是否17的倍數,就需要繼續上述「截尾、倍大、相減、驗差」的過程,直到能清楚判斷為止。例如,判斷1675282能不能被17整除,167528-2×5=167518,16751-8×5=16711,1671-1×5=1666,166-6×5=136,

136÷17=8,所以1675282能被17整除。

方法二:若一個整數的末三位與3倍的前面的隔出數的差能被17整除,則這個數能被17整除。例如,判斷1675282能不能被17整除,1675282的末三位是282,前面的數是1675,

282-1675×3=-4743,4743÷17=279,所以1675282能被17整除。

11、能19整除的數的特征。  

方法一:若一個整數的末三位與7倍的前面的隔出數的差能被19整除,則這個數能被19整除。例如,判斷234555能不能被19整除,234555末尾三位數是555,前面三位是234,

555-234×7=-1083,1083÷19=57,所以234555能被19整除。

方法二:若一個整數的個位數字截去,再從餘下的數中,加上個位數的2倍,如果和19的倍數,則原數能被19整除。如果和太大或心算不易看出是否19的倍數,就需要繼續上述「截尾、倍大、相加、驗和」的過程,直到能清楚判斷為止。
12、能23整除的數的特征。
    若一個整數的末四位與前面5倍的隔出數的差能被23(或29)整除,則這個數能被23整除。

13、能被25整除的數的特征。

如果一個數的末尾兩位能被25整除,則這個數能被25整除。

14、能被125整除的數的特征。

    如果一個數的末尾三位能被125整除,則這個數能被125整除。

 

 

分析2-為什麼將counter == 3判斷單獨拿出來就能求解了?

 

  考慮將每個數字模3以後的結果。

  如果對於當前數字能被3整除(0、3、6、9)模3結果為0的數,則直接個數加1

  如果是兩個數字那就有四種可能(1,1) (2, 2) (1, 2) (2, 1)其中後兩個組合之和為3能被3整除

  如果是三個數字對於之前兩個數字的組合只剩下(1, 1)和(2, 2) 那麼下個數不管是1還是2都可以組成一個3的整數倍

 

分析3-其他優質解法搜集:

  解法一:

    貪心思路:
  對每位數字對3求餘,則結果只能是0、1、2.如果當前位是0,則對結果貢獻為1,如果不為0,則判斷連續出現2或者1的數目num,如果num%3為0,則對結果的貢獻為num/3,否則可以找到一個與當前數字不相等的數字(前面連續出現的是2,當前為1。或者前面連續出現的是1,當前為2),則對結果貢獻是num/3+1.

#include<bits/stdc++.h>

using namespace std;
string s;

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> s;
    for(int i = 0; i < s.length(); i++) {
        s[i] = (s[i] - '0') % 3 + '0';
    }
    int num = 1;
    int ans = 0;
    for(int i = 1; i < s.length(); i++) {
        if(s[i] == s[i-1]) num++;
        else {
            if(s[i] == '0') {
                ans += num / 3;
                num = 1;
            }
            else if(s[i-1] == '0') {
                ans += num;
                num = 1;
            }
            else {
                ans += num / 3;
                if(num % 3) {
                    ans++;
                    num = 0;
                }
                else num = 1;
            }
        }
    }
    if(s[s.length()-1] == '0') ans += num;
    else ans += num / 3;
    cout << ans << endl;
    return 0;
}

  解法二:

  dp思路:
  dp[i]表示截止到下標為i的元素之前最多有多少個片段%3=0。很容易想到當(sum[i]-sum[j]) % 3 == 0時,dp[i] = max{dp[j]} + 1;所以用一個pre[]數組表示和當前sum值相等的上一個sum值,中間肯定經歷了一個(+3)%3的過程。所以最終的狀態方程是dp[i] = max(dp[pre[sum]]+1, dp[i]);

 

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1e7;
string s;
int dp[maxn], pre[maxn];

int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    memset(pre, -1, sizeof(pre));
    cin >> s;
    for(int i = 0; i < s.length(); i++) {
        s[i] = (s[i] - '0') % 3 + '0';
    }
    dp[0] = (s[0] == '0');
    int sum = s[0] - '0';
    pre[sum] = 0;
    for(int i = 1; i < s.length(); i++) {
        int t = s[i] - '0';
        sum = (sum + t) % 3;
        dp[i] = dp[i-1];
        if(sum == 0) dp[i] = max(dp[i], 1);
        if(pre[sum] != -1) dp[i] = max(dp[pre[sum]] + 1, dp[i]);
        pre[sum] = i; 
    } 
    cout << dp[s.length()-1] << endl;
    return 0;
}

 

  解法三:大師兄的神奇迴圈解法:

 

註:如果有更好的解法,真心希望您能夠評論留言貼上您的代碼呢~互相幫助互相鼓勵才能成長鴨~~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、什麼是單例模式?1、含義 作為對象的創建模式,單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統全局地提供這個實例。它不會創建實例副本,而是會向單例類內部存儲的實例返回一個引用。2、單例模式的三個要點:(1). 需要一個保存類的唯一實例的靜態成員變數:private static $_ ...
  • 一、背景 最初遇到這個問題是去58面試。部門領導是原同事,所以面試比較水。水到什麼程度呢? 面試就是走個形式而已,不會不過的。 一面面試官就問了一個問題:“一個請求過來都經過了什麼?” 剩下的全是閑聊。順便展示一下公司和部門的優勢。期待加入的意思。 聲明 面試如此之松是基於兩點: 第一點,與原同事多 ...
  • NLayerAppV3是一個使用.net 2.1實現的經典DDD的分層架構的項目。 ...
  • ""中文編程"知乎專欄原文" 源碼: "program in chinese/jinxiaocun" 由於這個演示項目成型於去年(詳見 "中文編程的嘗試歷程小記" ), Spring Boot還是老版本. 尚未將其更新到最新版本, 先將其中的一些中文命名的部分小結在此. URL 如: /商品表 /單 ...
  • 如果你要確定文件存在的話然後做些什麼,那麼使用try是最好不過的 如果您不打算立即打開文件,則可以使用os.path.isfile檢查文件 如果path是現有常規文件,則返回true。對於相同的路徑,islink()和isfile()都可以為true 如果你需要確定它是一個文件。 從Python 3 ...
  • 1.數值類型與進位 (1)基本類型 輸出結果: (2)進位 進位轉換 (3)基本運算 舍掉小數 輸出結果: 科學計數法 輸出結果: 2.運算符 (1)算術運算符 (2)比較運算符 (3)賦值運算符 (4)位運算符 我沒用過。。就不寫了。 (5)邏輯運算符 (6)成員運算符 (7)身份運算符 ...
  • ""中文編程"知乎專欄原文" 看到 "國人創造中文編程語言的優勢" 一文的評論後, 此文基於個人視野, 從幾個方面闡述中文編程興起的必然性和展望. 下麵是一些近十幾年中的相關趨勢. 對代碼可讀性的重視將會從大公司向小公司逐漸普及 在這個2010年的 "Quara回答" 中, Google已經把可讀性 ...
  • 方法的定義 如果沒有=和{}包裹的方法體,那麼該方法被隱式申明為抽象(abstract)方法,包含它的類就是抽象類。 當輸入相同類型的參數個數無法確定時,可以使用變長參數,如:def sum(args : Int*) = {for(arg <- args) println(arg)}。 如果方法體直 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...