標籤:
題意:有一些人,每人拿一個號碼,有兩個門,門的值分別為A和B,要求把人分成兩堆(可以為空白)一堆人手持號碼之和的數字根若等於A或者B就可以進入A門或者B門,要求兩堆人分別進入不同的門,求有幾種分配方式,如果A和B的值相等則算兩種。
解法:dp。比賽的時候並不知道一個數%9+1就是數字根……煞費苦心的算了半天……只得出結論a+b的數字根等於a的數字根+b的數字根的數字根……沒打表……於是T了……然後打了個表才過orz
設dp[i][j]表示用前i個人獲得數字根為j的和的方案數,打一個表vector<int> tab[i][j]表示和j相加的數字根等於i的數。則可以得到方程dp[i][j] = dp[i - 1][j] + ∑dp[i - 1][tab[j][a[i]][k]],dp[i - 1][j]表示不選第i個數,後面的和表示選第i個數時可以轉移來的所有狀態之和。
答案即為dp[n][A],但是當A和B相等時,多出一種A選0個人的方案,需要特判。
代碼:
因為一開始沒想到上面那個特判……想了好多亂七八糟的特判= =代碼好亂……【無視無視
#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cmath>#include <algorithm>#include <queue>#include <map>#include <set>#include <vector>#include <limits.h>using namespace std;typedef long long LL;const LL mod = 258280327;int t, n, A, B;int get_num(int x){ int tmp = x; while(tmp >= 10) { x = tmp; tmp = 0; while(x) { tmp += x % 10; x /= 10; } } return tmp;}vector <int> tab[10][10];void init(){ for(int i = 1; i <= 9; i++) for(int j = 1; j <= 9; j++) for(int k = 0; k <= 9; k++) { if(get_num(k + j) == i) tab[i][j].push_back(k); }}LL dp[100005][11];LL a[100005];int main(){ init(); scanf("%d", &t); while(t--) { scanf("%d%d%d", &n, &A, &B); memset(dp, 0, sizeof dp); LL sum = 0; for(int i = 1; i <= n; i++) { scanf("%lld", &a[i]); sum += a[i]; } for(int i = 0; i <= n; i++) dp[i][0] = 1; LL ans = 0; int tmp = get_num(sum); if(tmp != get_num(A + B)) { if(tmp == A && tmp == B) { printf("2\n"); continue; } else if(tmp == A) { printf("1\n"); continue; } else if(tmp == B) { printf("1Zn"); continue; } else { printf("0\n"); continue; } } if(sum == A) { if(sum == B) { printf("2\n"); continue; } else { printf("1\n"); continue; } } else if(sum == B) { printf("1\n"); continue; } for(int i = 1; i <= n; i++) { for(int j = 1; j <= 9; j++) { dp[i][j] += dp[i - 1][j]; int len = tab[j][a[i]].size(); for(int k = 0; k < len; k++) { dp[i][j] += dp[i - 1][tab[j][a[i]][k]]; if(dp[i][j] > mod) dp[i][j] -= mod; } } } ans = dp[n][A]; if(get_num(sum) == B) ans++; printf("%lld\n", ans); } return 0;}
HDU 5389 Zero Escape