Luogu P1874 快速求和 DP
阿新 • • 發佈:2020-08-14
傳送門
dp[i][j]表示前前i位組成數字k的最小次數,預處理出s[i][j]表示數位i到j表示的數字
狀態轉移方程即為:dp[i][k] = min(dp[i][k], dp[j - 1, k - s[i][j]] + 1).
dp[0][0] = 0, 其餘為大數字
當遇到s[i][j]大於n時,將s[i][j]設為一個大數,以免爆int
#include<cstdio> #include<cstring> #include<algorithm> #define INF 1000000 using namespace std; int num[100], len, n; int dp[50][100001], s[100][100]; //前i位組成數字k的最小次數 //dp[i][k] = min(dp[i][k], dp[i - void read(){ char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') num[++len] = ch - '0', ch = getchar(); return; } int main(){ for(int i = 0; i < 50; i++){ for(int j = 0; j < 100001; j++){ dp[i][j] = 1000000; } } for(int i = 0; i < 100; i++){ for(int j = 0; j < 100; j++){ s[i][j] = 1000000; } } read(); scanf("%d", &n); //[i, i+l]數字,若大於100000設為INF for(int i = 1; i <= len; i++){ s[i][i] = num[i]; } for(int l = 1; l <= len; l++){ for(int i = 1; i + l <= len; i++){ int tmp = s[i][i+l-1]*10 + num[i + l]; if(tmp > 100000ll) s[i][i+l] = INF; else s[i][i+l] = tmp; } } // for(int i = 1; i <= len; i++){ // for(int j = i; j <= len; j++){ // printf("s[%d][%d] = %d\n", i, j, s[i][j]); // } // } dp[0][0] = 0; for(int i = 1; i <= len; i++){ for(int j = 1; j <= i; j++){ // printf("%d %d\n", i, j); int val = s[j][i]; // printf("val = %d\n", val); if(s[j][i] > 100000) continue; for(int k = val; k <= n; k++){ // printf("k = %d\n", k); // printf("%d %d %d %d\n", j - 1, k - val, i, k); if(dp[j - 1][k - val] + 1 < dp[i][k]){ dp[i][k] = dp[j - 1][k - val] + 1; // printf("dp[%d][%d] = %d\n", i, k, dp[i][k]); } // dp[i][k] = min(dp[i][k], dp[j - 1][k - val] + 1); } // int l = i - j + 1; } } if(dp[len][n] > 100) dp[len][n] = 0; printf("%d", dp[len][n] - 1); return 0; }