動態規劃-牛客NC21302(被3整除的子序列)
阿新 • • 發佈:2021-09-06
還是先上題目
連結:https://ac.nowcoder.com/acm/problem/21302
來源:牛客網
題目描述
給你一個長度為50的數字串,問你有多少個子序列構成的數字可以被3整除答案對1e9+7取模
輸入描述:
輸入一個字串,由數字構成,長度小於等於50
輸出描述:
輸出一個整數示例1
輸入
複製132
輸出
複製3示例2
輸入
複製9
輸出
複製1示例3
輸入
複製333
輸出
複製7示例4
輸入
複製123456
輸出
複製23示例5
輸入
複製00
輸出
複製3
備註:
n為長度首先題目中的子序列並不是連續的子序列,中間是可以有跳躍的。 例如字串“134”,它的子串就有{1,3,4,13,14(這個子串就是不連續的,中間跳過了一個3),134}, 然後我們設an為長度是n的字串的子串數量,為了方便理解,下面我舉幾個例子;
子任務1: n <= 5
子任務2: n <= 20
子任務3: 無限制
這道題的難點我認為在於怎麼找到這個字串的所有子串是否能整除3。考慮到這題要用DP,我覺得應該是從最後一個大串往前推的,但是沒有思路。
看了大佬的解析,理解了他的思路,附上轉載“划水摸魚的文同學”的解析:
例:字串“3”,長度n=1,a1 |
例:
字串“123”,子串{1,2,3,12,13,23,123}
對3求餘為0的子串:3,12,123;(數量為3)
對3求餘為1的子串:1,13;(數量為2)
對3求餘為2的子串:2,23;(數量為2)
然後在字串“123”的基礎上加一個字元,這個要根據這個字元對3求餘的不同情況進行分別討論,
例1:”1234“(加入了新字元“4”,對3求余余1)
對3求餘為0的子串:3,12,123,24 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include<iostream>
#include<cstring>
//此題的子串並非連續子串,中間可有間隔;
//還有對中間的計算過程的數字對N求餘,不然資料中間可能會超,導致答案出錯
const int N=1e9+7;
using namespace std;
int main()
{
stringa;
cin>>a;
int lenth=a.size(),m=0; long long date[50][3]={0};
m=(a[0]- '0' )%3;
date[0][m]=1;
for ( int i=1;i<lenth;i++)
{
m=(a[i]- '0' )%3;
switch (m)
{
//新字元餘0的情況
case 0:
{
date[i][0]=(date[i-1][0]*2+1)%N;
date[i][1]=(date[i-1][1]*2)%N;
date[i][2]=(date[i-1][2]*2)%N;
break ;
}
//新字元餘1的情況
case 1:
{
date[i][0]=(date[i-1][0]+date[i-1][2])%N;
date[i][1]=(date[i-1][1]+date[i-1][0]+1)%N;
date[i][2]=(date[i-1][2]+date[i-1][1])%N;
break ;
}
//新字元餘2的情況
case 2:
{
date[i][0]=(date[i-1][0]+date[i-1][1])%N;
date[i][1]=(date[i-1][1]+date[i-1][2])%N;
date[i][2]=(date[i-1][2]+date[i-1][0]+1)%N;
break ;
}
}
}
cout<<date[lenth-1][0]%N; //輸出時取餘,按照題目要求
return 0;
}
|
這個程式碼非常易懂,解析也很明白。做完題後檢視別人程式碼,發現可以用巢狀for迴圈省略switch語句,/*優化演算法,避開switch,使用一個dp[i][m]=1做到原先的casej,box[i][j]+1 #include<bits/stdc++.h> #definemaxn500006 #defineinf0x3f3f3f3f3f3f3f3f//什麼意思? #definemod1000000007 #definePiacos(-1)//什麼意思? usingnamespacestd; typedeflonglongll; typedefpair<ll,string>P;//什麼? lldp[51][3]; intmain(){ ios::sync_with_stdio(false); strings; cin>>s; dp[0][(s[0]-'0')%3]=1; for(lli=1;i<s.size();i++){ llm=(s[i]-'0')%3; dp[i][m]=1; for(llj=0;j<3;j++){ dp[i][j]+=(dp[i-1][j]+dp[i-1][(j-m+3)%3])%mod; } } cout<<dp[s.size()-1][0]%mod<<endl; return0; } */
而在處理case[j]中date[i-1][j]=(......+1)的+1時,先採用了date[i][j]=1,再+=的思想,很美!
附上優化程式碼:(程式碼來自華夏Power)
但是有些地方還是看不懂這麼寫的意義,希望有大佬能評論回覆一下,學習一下!