【牛客 - 21302】被3整除的子序列(线性dp)
生活随笔
收集整理的這篇文章主要介紹了
【牛客 - 21302】被3整除的子序列(线性dp)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題干:
給你一個長度為50的數字串,問你有多少個子序列構成的數字可以被3整除
答案對1e9+7取模
輸入描述:
輸入一個字符串,由數字構成,長度小于等于50輸出描述:
輸出一個整數示例1
輸入
復制
132輸出
復制
3示例2
輸入
復制
9輸出
復制
1示例3
輸入
復制
333輸出
復制
7示例4
輸入
復制
123456輸出
復制
23示例5
輸入
復制
00輸出
復制
3備注:
n為長度 子任務1: n <= 5 子任務2: n <= 20 子任務3: 無限制解題報告:
? 不難發現長度為50的串最大可以到達的數字就是50*9 = 450 ,所以dp[i][j]代表前i個數 可以組成的和為j的方法數。然后分選和不選兩種決策轉移就行了。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #include<cctype> #define ll long long #define pb push_back #define pm make_pair using namespace std; const int MAX = 2e5 + 5; char s[MAX]; ll dp[55][555];//dp[i][j]代表前i個數 可以組成的和為j的方法數。 const ll mod = 1e9 + 7; int f(char c) {return c - '0'; } int main() {//dp[0][0]=1;cin>>(s+1);int len = strlen(s+1);for(int i = 1; i<=len; i++) { // dp[i][f(s[i])] = 1;for(int j = 0; j<555; j++) {dp[i][j] = dp[i-1][j];if(j == f(s[i])) dp[i][j]++;if(j >= f(s[i])) dp[i][j] += dp[i-1][j-f(s[i])];dp[i][j] %= mod;}}//printf("**%d\n",dp[len][3]);ll ans = 0;for(int j = 0; j<555; j++) if(j % 3 == 0) ans = (ans + dp[len][j]) % mod;printf("%lld\n",ans);return 0 ;}AC代碼2:(這樣寫更簡短一點)
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #include<cctype> #define ll long long #define pb push_back #define pm make_pair using namespace std; const int MAX = 2e5 + 5; char s[MAX]; ll dp[55][555];//dp[i][j]代表前i個數 可以組成的和為j的方法數。 const ll mod = 1e9 + 7; int f(char c) {return c - '0'; } int main() {//dp[0][0]=1;cin>>(s+1);int len = strlen(s+1);for(int i = 1; i<=len; i++) { // dp[i][f(s[i])] = 1;for(int j = 0; j<555; j++) {dp[i][j] = dp[i-1][j];if(j >= f(s[i])) dp[i][j] += dp[i-1][j-f(s[i])];dp[i][j] %= mod;}dp[i][f(s[i])]++;}ll ans = 0;for(int j = 0; j<555; j++) if(j % 3 == 0) ans = (ans + dp[len][j]) % mod;printf("%lld\n",ans); return 0 ;}?注意到這題不能直接想當然的dp[0][0]=0,,如果是這樣的話那代碼就更簡短了,,為什么不能這樣呢?因為這題中 0 也算狀態之一,所以不能這樣來初始化,考慮一般寫dp[0][0]的影響,無非就是加上自己的一次。所以我們雖然不寫dp[0][0]=0了,但是相應的把這個狀態自己加上就行了。注意AC代碼2,這一句++必須放在for之后,不能放在for之前,因為不然后面for中那個等號就把這個++ 操作給覆蓋了。(其實放在for之前也行,但是就得變成dp[i][j] += dp[i-1][j];了?)
同時通過觀察我們發現,最后答案只跟模數有關,所以我們直接記錄模數也可以。
AC代碼3:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #include<cctype> #define ll long long #define pb push_back #define pm make_pair using namespace std; const int MAX = 2e5 + 5; char s[MAX]; ll dp[55][3];//dp[i][j]代表前i個數 可以組成的和為j的方法數。 const ll mod = 1e9 + 7; int f(char c) {return c - '0'; } int main() {//dp[0][0]=1;cin>>(s+1);int len = strlen(s+1);for(int i = 1; i<=len; i++) { // dp[i][f(s[i])] = 1;for(int j = 0; j<3; j++) {dp[i][j] = dp[i-1][j];dp[i][j] += dp[i-1][(j+15-f(s[i]))%3];dp[i][j] %= mod;}dp[i][f(s[i])%3]++;}ll ans = dp[len][0];//for(int j = 0; j<555; j++) if(j % 3 == 0) ans = (ans + dp[len][j]) % mod;printf("%lld\n",ans%mod); return 0 ;}?
總結
以上是生活随笔為你收集整理的【牛客 - 21302】被3整除的子序列(线性dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: *【CodeForces - 859C
- 下一篇: switpa.exe - switpa是