P1831 杠杆数
是否说出正解: ×
显然是个数位DP,但是感觉无从下手,事实上可以做18次数位dp,每次枚举支点,就可以算出每个数的力矩,取出力矩为0的作为贡献即可

#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <vector> #include <string> #include <bitset> #include <cstdio> #include <cstdlib> #include <cstring> #include <sstream> #include <iostream> #include <algorithm> #include <functional> using namespace std; #define For(i, x, y) for(int i=x;i<=y;i++) #define _For(i, x, y) for(int i=x;i>=y;i--) #define Mem(f, x) memset(f,x,sizeof(f)) #define Sca(x) scanf("%d", &x) #define Sca2(x,y) scanf("%d%d",&x,&y) #define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define Scl(x) scanf("%lld",&x) #define Pri(x) printf("%d ", x) #define Prl(x) printf("%lld ",x) #define CLR(u) for(int i=0;i<=N;i++)u[i].clear(); #define LL long long #define ULL unsigned long long #define mp make_pair #define PII pair<int,int> #define PIL pair<int,long long> #define PLL pair<long long,long long> #define pb push_back #define fi first #define se second typedef vector<int> VI; LL read(){LL x = 0,f = 1;char c = getchar();while (c<'0' || c>'9'){if (c == '-') f = -1;c = getchar();} while (c >= '0'&&c <= '9'){x = x * 10 + c - '0';c = getchar();}return x*f;} const double PI = acos(-1.0); const double eps = 1e-9; const int maxn = 110; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; int N,M,K,now; int str[20]; LL dp[20][3000]; LL dfs(int pos,int val,int limit){ if(!pos) return val == 0; if(val < 0) return 0; if(!limit && ~dp[pos][val]) return dp[pos][val]; LL ans = 0; int up = limit?str[pos]:9; for(int i = 0 ; i <= up; i ++){ ans += dfs(pos - 1,val + i * (pos - now),limit && (i == up)); } if(!limit) dp[pos][val] = ans; return ans; } LL solve(LL x){ if(!x) return 1; int l = 0; LL m = x; while(m){ l++; str[l] = m % 10; m /= 10; } LL ans = 0; for(int i = 1; i <= l; i ++){ Mem(dp,-1); now = i; ans += dfs(l,0,1); } return ans - (l - 1); } int main(){ LL l = read(),r = read(); Prl(solve(r) - solve(l - 1)); return 0; }
P2939 [USACO09FEB]改造路Revamping Trails
是否说出正解:√
很原题了 最短路维护dis[i][j]表示到i点已经经过了j条高速公路的最短路, Dijkstra直接维护即可
P2198 杀蚂蚁
是否说出正解:√
似乎dp[x][y]表示经过了x个放射塔,y个放射塔的最小值即可,干扰塔数量用n - x - y表示
dp[x][y] = min(dp[x - 1][y] + (x - 1) * g * (t + y * b),dp[x][y - 1] + x * g * (t + (y - 1) * b,dp[x][y] + (x + r) * g * (t + y * b)
P4766 [CERC2014]Outer space invaders
是否说出正解: ×
离散化之后区间dp

#include<bits/stdc++.h> using namespace std; const int maxn = 6010; const int INF = 0x3f3f3f3f; int N; struct Node{ int l,r,d; Node(int l = 0,int r = 0,int d = 0):l(l),r(r),d(d){} }node[maxn]; int Hash[maxn]; int dp[maxn][maxn]; int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d",&N); int cnt = 0; for(int i = 1; i <= N; i ++){ scanf("%d%d%d",&node[i].l,&node[i].r,&node[i].d); Hash[++cnt] = node[i].l; Hash[++cnt] = node[i].r; } sort(Hash + 1,Hash + 1 + cnt); cnt = unique(Hash + 1,Hash + 1 + cnt) - Hash - 1; for(int i = 1; i <= N ; i ++){ node[i].l = lower_bound(Hash + 1,Hash + 1 + cnt,node[i].l) - Hash; node[i].r = lower_bound(Hash + 1,Hash + 1 + cnt,node[i].r) - Hash; } for(int len = 1; len <= cnt; len ++){ for(int l = 1; l + len - 1 <= cnt; l ++){ int r = l + len - 1; int id = 0; for(int i = 1; i <= N ; i ++){ if(node[i].l < l || node[i].r > r) continue; if(!id || node[id].d < node[i].d) id = i; } if(!id){ dp[l][r] = 0; continue; } dp[l][r] = INF; for(int k = node[id].l; k <= node[id].r; k ++){ dp[l][r] = min(dp[l][r],dp[l][k - 1] +dp[k + 1][r] + node[id].d); } } } printf("%d ",dp[1][cnt]); } return 0; }
P2400 秘密文件
是否说出正解:√
利用KMP的next数组求出每个子串的循环节,然后区间dp
P4046 [JSOI2010]快递服务
是否说出正解:√
从给定的顺序开始递推,dp[i][j]表示除了一个司机必定在上一个点之外,其他两个司机分别在第i,j的位置,因为每一层之间独立,所以需要开滚动数组
时间复杂度O(n³)
P3413 SAC#1 - 萌数
是否说出正解:√
显然是数位dp,意识到最短回文串只需要关注两位数字和三位数字即可,即存在一个数和前一位或者前前一位相等即可。
那么dp[1000][11][11]表示当前位置pos上一位数字为i,上上位数字为j有多少种情况然后去记忆化搜索即可。
因为数位dp并不熟练,所以敲了一发

#include<bits/stdc++.h> using namespace std; #define LL long long const int maxn = 1010; const int mod = 1e9 + 7; char s1[maxn],s2[maxn]; int str[maxn]; LL dp[maxn][11][11]; LL ten[maxn],num[maxn]; int L; LL dfs(int pos,int la,int lla,bool limit,bool zero){ if(pos == L) return 0; if(~dp[pos][la][lla] && !limit && !zero) return dp[pos][la][lla]; int top = limit?str[pos]:9; LL ans = 0; for(int i = 0; i <= top; i ++){ if(i == la || i == lla){ if(limit && i == str[pos]){ ans = (ans + num[pos + 1] + 1) % mod; // cout << pos << " " << num[pos + 1] << endl; } else ans = (ans + ten[pos]) % mod; continue; } ans = (ans + dfs(pos + 1,(zero && !i)?10:i,la,limit && i == str[pos],zero && !i)) % mod; } if(!limit && !zero) dp[pos][la][lla] = ans; return ans; } LL solve(char *s){ int l = strlen(s); L = l; ten[l - 1] = 1; num[l] = 0; for(int i = l - 1; i >= 0; i --){ str[i] = s[i] - '0'; num[i] = (num[i + 1] + (str[i] * ten[i]) % mod) % mod; if(i) ten[i - 1] = ten[i] * 10 % mod; } for(int i = 0 ; i <= l; i ++){ for(int j = 0 ; j <= 10; j ++) for(int k = 0 ; k <= 10; k ++) dp[i][j][k] = -1; } return dfs(0,10,10,1,1); } int check(char *s){ int l = strlen(s); for(int i = 1; i < l; i ++){ if(s[i] == s[i - 1]) return true; if(i > 1 && s[i] == s[i - 2]) return true; } return false; } int main(){ scanf("%s%s",s1,s2); printf("%lld ",(solve(s2)-solve(s1) + check(s1) + mod) % mod); return 0; }
P4290 [HAOI2008]玩具取名
比较裸的区间dp dp[l][r][x]表示区间[l,r]合成字母x的可行性

#include<bits/stdc++.h> using namespace std; const int maxn = 220; int N; const char c[5] = {'0','W','I','N','G'}; map<char,int>id; int num[5]; char str[5]; char s[maxn]; bool change[5][5][5]; bool dp[maxn][maxn][5]; int main(){ id['W'] = 1; id['I'] = 2; id['N'] = 3; id['G'] = 4; for(int i = 1; i <= 4; i ++) scanf("%d",&num[i]); for(int i = 1; i <= 4; i ++){ for(int j = 1; j <= num[i]; j ++){ scanf("%s",str); change[id[str[0]]][id[str[1]]][i] = 1; } } scanf("%s",s + 1); int L = strlen(s + 1); for(int i = 1; i <= L ; i ++) dp[i][i][id[s[i]]] = 1; for(int len = 2; len <= L; len++){ for(int l = 1; l + len - 1<= L; l ++){ int r = l + len - 1; for(int k = l; k < r; k ++){ for(int p = 1; p <= 4; p ++){ if(!dp[l][k][p]) continue; for(int q = 1; q <= 4; q ++){ if(!dp[k + 1][r][q]) continue; for(int x = 1; x <= 4; x ++){ if(change[p][q][x]) dp[l][r][x] = 1; } } } } } } bool flag = 0; for(int i = 1; i <= 4; i ++) if(dp[1][L][i]){ putchar(c[i]); flag = 1; } if(!flag) puts("The name is wrong!"); return 0; }
P1121 环状最大两段子段和
枚举中间点,求中间点往左的最大字段和加上中间点往右的最大子段和的最大值。
如果字段经过了环,就用将字段取反,用同样的方法求两段最大值,表示这两段不取。