紫书 动态规划例题
https://vjudge.net/contest/176767#overview
A - A Spy in the Metro
#include <bits/stdc++.h> using namespace std; const int N = 55, M = 205; int t[N], d[N][M]; //j时刻在i号车站剩下的最小总等待时间 bool l[N][M], r[N][M]; //j时刻在i号车站是否有往左(右)的车 int main() { int n, m, ti, cur, cas = 0; while(~scanf("%d", &n), n) { scanf("%d", &ti); memset(l, 0, sizeof(l)), memset(r, 0, sizeof(r)); for(int i = 1; i < n; ++i) scanf("%d", &t[i]); scanf("%d", &m); //cur时刻车站j是否有往右的车 for(int i = 1; i <= m; ++i) { scanf("%d", &cur); for(int j = 1; j <= n; ++j) r[j][cur] = 1, cur += t[j]; } scanf("%d", &m); //cur时刻车站j是否有往左的车 for(int i = 1; i <= m; ++i) { scanf("%d", &cur); for(int j = n; j >= 1; --j) l[j][cur] = 1, cur += t[j - 1]; } memset(d, 0x3f, sizeof(d)); d[n][ti] = 0; for(int j = ti - 1; j >= 0; --j) { for(int i = 1; i <= n; ++i) { d[i][j] = d[i][j + 1] + 1; //在i车站等1单位时间 if(l[i][j]) d[i][j] = min(d[i][j], d[i - 1][j + t[i - 1]]); //往左 if(r[i][j]) d[i][j] = min(d[i][j], d[i + 1][j + t[i]]); //往右 } } printf("Case Number %d: ", ++cas); if(d[1][0] > ti) puts("impossible"); else printf("%d\n", d[1][0]); } return 0; }
B
最长递增子序列变形
C
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<sstream> #include<algorithm> #include<queue> #include<deque> #include<iomanip> #include<vector> #include<cmath> #include<map> #include<stack> #include<set> #include<memory> #include<list> #include<string> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define MAXN 1009 #define L 31 #define INF 1000000009 #define eps 0.00000001 int n; double dp[MAXN][MAXN]; double dist[MAXN][MAXN]; struct node { double x, y; }a[MAXN]; double D(const node & a, const node &b) { return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)); } int main() { while (scanf("%d", &n) != EOF) { for (int i = 1; i <= n; i++) { scanf("%lf%lf", &a[i].x, &a[i].y); for (int j = 1; j < i; j++) dist[i][j] = D(a[i], a[j]); } for (int i = n - 1; i >= 2; i--) { for (int j = 1; j < i; j++) { if (i == n - 1) dp[i][j] = dist[n][i] + dist[n][j]; else dp[i][j] = min(dp[i + 1][j] + dist[i + 1][i], dp[i + 1][i] + dist[i + 1][j]); } } printf("%.2lf\n", dist[2][1] + dp[2][1]); } }
D
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<sstream> #include<algorithm> #include<queue> #include<deque> #include<iomanip> #include<vector> #include<cmath> #include<map> #include<stack> #include<set> #include<memory> #include<list> #include<string> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define MAXN 1009 #define L 31 #define INF 1000000009 #define eps 0.00000001 /* 单向TSP 书上给的是反向DP 这样更好输出解 */ int n, m; int a[11][MAXN], dp[11][MAXN], path[11][MAXN]; int main() { while (scanf("%d%d", &m, &n) != EOF)//m行n 列 { for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { scanf("%d", &a[i][j]); } } int ans = INF, first = 0; for (int j = n - 1; j >= 0; j--) { for (int i = 0; i < m; i++) { if (j == n - 1) dp[i][j] = a[i][j]; else { int row[3] = { i + 1,i - 1,i }; if (i == 0) row[1] = m - 1; if (i == m - 1) row[0] = 0; sort(row, row + 3); dp[i][j] = INF; for (int k = 0; k < 3; k++) { int v = dp[row[k]][j + 1] + a[i][j]; if (v < dp[i][j]) { dp[i][j] = v, path[i][j] = row[k]; } } } if (j == 0 && dp[i][0] < ans) { ans = dp[i][0], first = i; } } } printf("%d", first + 1); for (int i = path[first][0], j = 1; j < n; i = path[i][j], j++) { printf(" %d", i + 1); } printf("\n%d\n", ans); } }
E
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<sstream> #include<algorithm> #include<queue> #include<deque> #include<iomanip> #include<vector> #include<cmath> #include<map> #include<stack> #include<set> #include<memory> #include<list> #include<string> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define MAXN 1009 #define L 31 #define INF 1000000009 #define eps 0.00000001 /* 对于某一种灯泡 要么全部替换 要么全都不替换 证明:如果替换N个剩下M个能带来 P*N的收益,那么替换剩下的M个可以带来P*M+K的收益。K是节约掉的搭建电源的收益 dp[i]表示满足前i种灯泡需求的花费 区域考虑!考虑某一片都用这个灯泡 */ struct node { int v, k, c, l; bool operator<(const node& rhs) { return v < rhs.v; } }a[MAXN]; int dp[MAXN], pre[MAXN], n; int main() { while (scanf("%d", &n), n) { memset(pre, 0, sizeof(pre)); memset(dp, INF, sizeof(dp)); for (int i = 1; i <= n; i++) scanf("%d%d%d%d", &a[i].v, &a[i].k, &a[i].c, &a[i].l); sort(a + 1, a + n + 1); int sum = 0; dp[0] = 0; for (int i = 1; i <= n; i++) { pre[i] = pre[i - 1] + a[i].l; } for (int i = 1; i <= n; i++) { for (int j = 0; j < i; j++) { dp[i] = min(dp[j] + (pre[i] - pre[j])*a[i].c + a[i].k,dp[i]); } } printf("%d\n", dp[n]); } }
G
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<sstream> #include<algorithm> #include<queue> #include<deque> #include<iomanip> #include<vector> #include<cmath> #include<map> #include<stack> #include<set> #include<memory> #include<list> #include<string> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define MAXN 1009 #define L 31 #define INF 1000000009 #define eps 0.00000001 /* 先用n2 时间维护一个二维数组记录i-j是不是回文字串 然后dp */ char str[MAXN]; int dp[MAXN]; bool vis[MAXN][MAXN]; int main() { int n; scanf("%d", &n); while (n--) { memset(vis, false, sizeof(vis)); scanf("%s", str + 1); int l = strlen(str + 1); for (int i = 1; i <= l; i++) vis[i][i] = true; for (int i = 1; i < l; i++) { if (str[i] == str[i + 1]) vis[i][i + 1] = true; else vis[i][i + 1] = false; } for (int k = 2; k <= l; k++) { for (int i = 1; i <= l; i++) { int j = i + k; if (j > l) break; if (str[i] == str[j] && vis[i + 1][j - 1]) vis[i][j] = true; } } memset(dp, INF, sizeof(dp)); dp[0] = 0; for (int i = 1; i <= l; i++) { for (int j = 0; j < i; j++) { if (vis[j+1][i]) dp[i] = min(dp[i], dp[j] + 1); } } printf("%d\n", dp[l]); } }
H
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #define INF 10e7 using namespace std; int T,len1,len2; int dp[5005][5005]; int st1[27],st2[27],ed1[27],ed2[27]; char s1[5005],s2[5005]; void dp_solve() { //int cnt=0,res=INF; for(int i=0; i<=len1; i++) for(int j=0; j<=len2; j++) { int cnt=0,res=INF; for(int k=0; k<26; k++) if((i>=st1[k]||j>=st2[k])&&(i<ed1[k]||j<ed2[k]))//此处判断有多少种颜色已经出现但尚未结束 cnt++; if(i>0) res=min(res,dp[i-1][j]); if(j>0) res=min(res,dp[i][j-1]); dp[i][j]=cnt+(res==INF ? 0:res); } cout<<dp[len1][len2]<<endl; } int main() { cin>>T; while(T--) { scanf("%s%s",s1+1,s2+1); len1=strlen(s1+1); len2=strlen(s2+1); for(int i=0; i<26; i++) { st1[i]=st2[i]=INF; ed1[i]=ed2[i]=0; } int tmp; for(int i=1; i<=len1; i++) { tmp=s1[i]-'A'; if(st1[tmp]==INF) st1[tmp]=i; ed1[tmp]=i; } for(int i=1; i<=len2; i++) { tmp=s2[i]-'A'; if(st2[tmp]==INF) st2[tmp]=i; ed2[tmp]=i; } dp_solve(); } return 0; }
I
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #define INF 10e7 using namespace std; int T,len1,len2; int dp[5005][5005]; int st1[27],st2[27],ed1[27],ed2[27]; char s1[5005],s2[5005]; void dp_solve() { //int cnt=0,res=INF; for(int i=0; i<=len1; i++) for(int j=0; j<=len2; j++) { int cnt=0,res=INF; for(int k=0; k<26; k++) if((i>=st1[k]||j>=st2[k])&&(i<ed1[k]||j<ed2[k]))//此处判断有多少种颜色已经出现但尚未结束 cnt++; if(i>0) res=min(res,dp[i-1][j]); if(j>0) res=min(res,dp[i][j-1]); dp[i][j]=cnt+(res==INF ? 0:res); } cout<<dp[len1][len2]<<endl; } int main() { cin>>T; while(T--) { scanf("%s%s",s1+1,s2+1); len1=strlen(s1+1); len2=strlen(s2+1); for(int i=0; i<26; i++) { st1[i]=st2[i]=INF; ed1[i]=ed2[i]=0; } int tmp; for(int i=1; i<=len1; i++) { tmp=s1[i]-'A'; if(st1[tmp]==INF) st1[tmp]=i; ed1[tmp]=i; } for(int i=1; i<=len2; i++) { tmp=s2[i]-'A'; if(st2[tmp]==INF) st2[tmp]=i; ed2[tmp]=i; } dp_solve(); } return 0; }
I 树形DP
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<sstream> #include<algorithm> #include<queue> #include<deque> #include<iomanip> #include<vector> #include<cmath> #include<map> #include<stack> #include<set> #include<memory> #include<list> #include<string> using namespace std; typedef long long LL; typedef unsigned long long ULL; #define MAXN 55 #define L 31 #define INF 1000000009 #define eps 0.00000001 /* dp[i][j] 为切割小木棍i-j点的费用 dp[i][j] = a[j]-a[i](切割第一刀的费用) + dp[i][k] + dp[k][j] 从中间的k点切 O(n3) */ int l, n, a[MAXN]; int dp[MAXN][MAXN]; int main() { while (scanf("%d", &l), l) { memset(dp, INF, sizeof(dp)); memset(a, 0, sizeof(a)); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 0; i <= n + 1; i++) dp[i][i] = 0; for (int i = 0; i < n + 1; i++) dp[i][i + 1] = a[i + 1] - a[i]; a[0] = 0, a[n + 1] = l; for (int k = 1; k <= n + 1; k++) { for (int i = 0; i + k <= n + 1; i++) { int j = i + k; for (int t = i + 1; t < j; t++) dp[i][j] = min(dp[i][j], dp[i][t] + dp[t][j] + a[j] - a[i]); } } printf("The minimum cutting is %d.\n", dp[0][n + 1]); } }