『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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...