zoukankan      html  css  js  c++  java
  • 动态规划例题总结

    Uva1025
    题意:这个城市有n个车站,每隔一段时间会从所有车站的一遍发出一列列车,有一个要从第一个车站出发,目的是在时间T会见车站n 的一个人,需要换乘,问在不同车站换成所需要等待的最短时间是多少

    解法:d(i,j)表示在时刻i,你在j车站所需要等待的最长时间,有三种决策,1. 等一分钟、2. 搭乘往右开的车、3. 搭乘往左开的车

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 using namespace std;
     5 const int inf = 0x3fffffff;
     6 int dp[200 + 5][50 + 5];//在时间t,车站i应该等待的最长时间
     7 int has_train[205][55][2], t[50 + 5];//has_train表示在时间t车站i是否有列车
     8 int d[50 + 5], e[50 + 5];//往右开的第一班车,往左开的第一班车
     9 
    10 int main() {
    11     int n, T, tmp = 1;
    12     while (scanf("%d%d", &n, &T)&&n) {
    13         memset(dp, 0, sizeof(dp)); memset(has_train, 0, sizeof(has_train));
    14         memset(t, 0, sizeof(t)); memset(d, 0, sizeof(d)); memset(e, 0, sizeof(e));
    15 
    16         for (int i = 1; i <= n - 1; i++) scanf("%d", &t[i]);
    17 
    18         int m1; scanf("%d", &m1);
    19         for (int i = 1; i <= m1; i++) {
    20             scanf("%d", &d[i]);
    21             int tmp = d[i];
    22             for (int j = 1; j <= n - 1; j++) {
    23                 if (tmp <= T) has_train[tmp][j][0] = 1;
    24                 tmp += t[j];
    25             }
    26         }
    27         int m2; scanf("%d", &m2);
    28         for (int i = 1; i <= m2; i++) {
    29             scanf("%d", &e[i]);
    30             int tmp = e[i];
    31             for (int j = n - 1; j >= 1; j--) {
    32                 if (tmp <= T) has_train[tmp][j + 1][1] = 1;
    33                 tmp += t[j];
    34             }
    35         }
    36 
    37         for (int i = 1; i <= n - 1; i++)dp[T][i] = inf;
    38         dp[T][n] = 0;
    39 
    40         for (int i = T - 1; i >= 0; i--)
    41             for (int j = 1; j <= n; j++) {
    42                 dp[i][j] = dp[i + 1][j] + 1;//时间先前流动一秒
    43                 if (j < n&&has_train[i][j][0] && i + t[j] <= T)
    44                     dp[i][j] = min(dp[i][j], dp[i + t[j]][j + 1]);
    45                 if (j > 1 && has_train[i][j][1] && i + t[j - 1] <= T)
    46                     dp[i][j] = min(dp[i][j], dp[i + t[j - 1]][j - 1]);
    47             }
    48 
    49         printf("Case Number %d: ", tmp++);
    50         if (dp[0][1] >= inf) printf("impossible
    ");
    51         else printf("%d
    ", dp[0][1]);
    52     }
    53     return 0;
    54 }

    Uva437
    题意:有n种正方体,每种都有无穷多个。要求选一些立方体摞成一根尽量高的柱子,使得每个立方体的地面长宽严格小于它下方立方体的地面长宽

    解法:见代码

     1 /*
     2 思路:
     3 因为a和b的值会很大,所以我们不能直接用d(a,b)来表示状态值;
     4 所以我们(idx,h)来表示这个状态,h存放的是立方体的的三条高,知道其中的一条我们就能知道对应的面
     5 idx表示立方体的编号
     6 ans就是我们要求的那个结果,即最大高度,状态转移就是简单的拿或者不拿那个立方体
     7 */
     8 
     9 #include<cstdio>
    10 #include<algorithm>
    11 #include<cstring>
    12 using namespace std;
    13 const int maxn = 30 + 5;
    14 int n, blocks[maxn][3], d[maxn][3];
    15 
    16 void get_dimensions(int* v, int b, int dim) {
    17     int idx = 0;
    18     for (int i = 0; i < 3; i++) {
    19         if (i != dim) v[idx++] = blocks[b][i];
    20     }
    21 }
    22 
    23 int dp(int i,int j) {
    24     int&ans = d[i][j];
    25     if (ans > 0)return ans;
    26     ans = 0;
    27     int v[2], v2[2];
    28     get_dimensions(v, i, j);
    29     for(int a=0;a<n;a++)
    30     for(int b=0;b<3;b++){//a和b是底面的边长
    31         get_dimensions(v2, a, b);
    32         if (v2[0] < v[0] && v2[1] < v[1]) ans = max(ans, dp(a, b));
    33     }
    34     ans += blocks[i][j];
    35     return ans;
    36 }
    37 
    38 int main() {
    39     int kase = 0;
    40     while (scanf("%d", &n) && n) {
    41         for (int i = 0; i < n; i++) {
    42             for (int j = 0; j < 3; j++) 
    43                 scanf("%d", &blocks[i][j]);
    44             sort(blocks[i], blocks[i] + 3);
    45         }
    46         memset(d, 0, sizeof(d));
    47         int ans = -1;
    48         for (int i = 0; i < n; i++) {
    49             for (int j = 0; j < 3; j++) {
    50                 ans = max(ans, dp(i, j));
    51             }
    52         }
    53         printf("Case %d: maximum height = %d
    ", ++kase, ans);
    54     }
    55     return 0;
    56  }

    Uva116
    题意:有一个m行n列的整数矩阵,从第一列任何一个位置出发每次往↗↘或者→走一格,最终到达最后一列,要求经过的整数之和最小。整个矩形是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解释输出字典序最小的

    解法:见代码

     1 /*
     2 多阶段决策问题,重点是行和列的处理,注意看一下
     3 还有一个重点是结点路径的保存
     4 */
     5 #include<cstdio>
     6 #include<algorithm>
     7 #include<cstring>
     8 using namespace std;
     9 const int inf = 0x3fffffff;
    10 int a[1010][1010], d[1010][1010], nex[1010][1010];
    11 
    12 int main() {
    13     int n, m;
    14     while (scanf("%d%d", &m, &n) ==2 && m ) {//m行,n列
    15         memset(a, 0, sizeof(a)); memset(d, 0, sizeof(d));
    16         memset(nex, 0, sizeof(nex));
    17 
    18         for (int i = 0; i < m; i++) 
    19             for (int j = 0; j < n; j++) {
    20                 scanf("%d", &a[i][j]);
    21             }
    22 
    23         int ans = inf, first = 0;
    24         for (int j = n - 1; j >= 0; j--) {//从每一行开始
    25             for (int i = 0; i < m; i++) {//从每一列开始
    26                 if (j == n - 1)d[i][j] = a[i][j];//设置初始值
    27                 else {
    28                     int row[3] = { i,i - 1,i + 1 };//三个方向
    29                     if (i == 0)row[1] = m - 1;//上一行是下边界
    30                     if (i == m - 1)row[2] = 0;//下一行是上边界
    31                     sort(row, row + 3);
    32                     d[i][j] = inf;
    33                     for (int k = 0; k < 3; k++) {
    34                         int v = d[row[k]][j + 1] + a[i][j];
    35                         //↓↓↓nex[i][j]表示从(i,j)出发的下一个坐标
    36                         if (v < d[i][j]) { d[i][j] = v; nex[i][j] = row[k]; }
    37                     }
    38                 }
    39                 if (j == 0 && d[i][j] < ans) { ans = d[i][j]; first = i; }//更新出发点
    40             }
    41         }
    42 
    43         printf("%d", first + 1);
    44         for (int i = nex[first][0], j = 1; j < n; i = nex[i][j], j++) {
    45             printf(" %d", i + 1);
    46         }printf("
    %d
    ", ans);
    47     }
    48     return 0;
    49 }

    Uva12563
    题意:有n首歌,剩余时间为UP,每首歌都有一个时间长度,要求你在剩余时间内尽可能多的唱歌,同时要求唱的时间尽可能的长

    解法:01背包,双状态

     1 /*
     2 背包问题的变式,这道题的解法涉及到了一个dp中核心的解法:状态
     3 注意枚举上界UP要-1
     4 */
     5 #include<cstdio>
     6 #include<algorithm>
     7 #include<cstring>
     8 using namespace std;
     9 typedef long long ll;
    10 const int maxn = 180 * 50 + 5;
    11 struct node {
    12     int num, len;
    13     bool operator <  (const node& rhs) const {
    14         return num < rhs.num || (num == rhs.num&&len < rhs.len);
    15     }
    16 };
    17 node dp[maxn];
    18 ll t[maxn];
    19 
    20 int main() {
    21     int T; scanf("%d", &T);
    22     int kase = 1;
    23     while (T--) {
    24         ll n, UP; scanf("%lld%lld", &n, &UP);
    25         memset(t, 0, sizeof(t)); memset(dp, 0, sizeof(dp));
    26         ll sum = 0;
    27         for (int i = 1; i <= n; i++) scanf("%lld", &t[i]), sum += t[i];
    28         UP -= 1;
    29 
    30         for (int i = 1; i <= n; i++) {
    31             for (int j = UP; j >= 0; j--) {
    32                 if (j < t[i])break;//剩余时间都不够t[i]了,没必要再选择了
    33                 node tmp = { dp[j - t[i]].num + 1,dp[j - t[i]].len + t[i] };
    34                 dp[j] = max(dp[j], tmp);//选取更优状态
    35             }
    36         }
    37 
    38         printf("Case %d: %d %d
    ", kase++, dp[UP].num + 1, dp[UP].len + 678);
    39     }
    40     return 0;
    41 }

    Uva1625
    题意:有两个字符串,每次可以选两个首位的字符将他们加入到新串的尾部,对于每个颜色来说(每个颜色就是每个字符),其跨度L(c)等于最大位置和最小位置之差。找到一种合并方式,使得所有的L(c)总和最小

    解法:见代码

     1 /*
     2 非常好的思路题,多多品味
     3 */
     4 #include<cstdio>
     5 #include<algorithm>
     6 #include<cstring>
     7 using namespace std;
     8 const int maxn = 5e3 + 5;
     9 const int inf = 0x3fffffff;
    10 int dp[maxn][maxn], cnt[maxn][maxn];
    11 int bf[100], bs[100], ef[100], es[100];
    12 char f[maxn], s[maxn];
    13 //dp[i][j]为从第一个颜色序列中拿走前i个颜色,从第二个颜色中拿走前j个颜色时合并产生的序列的最小lc之和
    14 //cnt[i][j]为分别取走i个颜色和j个颜色时还有多少种颜色已经出现但尚未结束
    15 //bf[c]为f串中c字母出现的第一个位置,es[c]为f串中c字母出现的最后一个位置
    16 //ef同上,只不过对应s串中的情况
    17 
    18 int main() {
    19     int T; scanf("%d", &T);
    20     while (T--) {
    21         scanf("%s%s", f + 1, s + 1);
    22         int lenf = strlen(f+1), lens = strlen(s+1);
    23         for (char c = 'A'; c <= 'Z'; c++) {
    24             bf[c] = bs[c] = inf;
    25             ef[c] = es[c] = 0;
    26         }
    27 
    28         //处理开始值和终点值,注意处理开始值的时候采用最值的方法可以避免定义first变量
    29         for (int i = 1; i <= lenf; i++) {
    30             char c = f[i];
    31             bf[c] = min(i, bf[c]);
    32             ef[c] = i;
    33         }
    34         for (int i = 1; i <= lens; i++) {
    35             char c = s[i];
    36             bs[c] = min(i, bs[c]);
    37             es[c] = i;
    38         }
    39 
    40         //处理cnt数组
    41         for (int i = 0; i <= lenf; i++) 
    42             for (int j = 0; j <= lens; j++) {
    43                 if (i) {
    44                     cnt[i][j] = cnt[i - 1][j];//取走f数组中的一个颜色
    45                     char c = f[i];
    46                     if (bf[c] == i&&bs[c] > j)cnt[i][j]++;//f中已经出现但是s中没有出现
    47                     if (ef[c] == i&&es[c] <= j)cnt[i][j]--;//f中已经结束或者s中已经结束
    48                     //上面这个还值得推敲
    49                 }
    50                 if (j) {
    51                     cnt[i][j] = cnt[i][j - 1];
    52                     char c = s[j];
    53                     if (bs[c] == j&&bf[c] > i)cnt[i][j]++;
    54                     if (es[c] == j&&ef[c] <= i)cnt[i][j]--;
    55                 }
    56             }
    57 
    58         for (int i = 0; i <= lenf; i++) 
    59             for (int j = 0; j <= lens; j++) {
    60                 if (!i && !j)continue;
    61                 dp[i][j] = inf;
    62                 //这点非常重要,+cnt[i][j]其实将所有的字母的情况都考虑进去了
    63                 if (i)dp[i][j] = min(dp[i][j], dp[i - 1][j] + cnt[i - 1][j]);
    64                 if (j)dp[i][j] = min(dp[i][j], dp[i][j - 1] + cnt[i][j - 1]);
    65             }
    66         printf("%d
    ", dp[lenf][lens]);
    67     }
    68     return 0;
    69 }
  • 相关阅读:
    快速排序
    visio中相关设置菜单视图
    Visio中ShapeAdded和SelectionAdded
    VB托盘图标不响应WM_MOUSEMOVE的原因及解决方法
    visio应用程序相关设置选项视图
    直接插入排序的三种实现
    计算机存储的大小端模式解析
    直接选择排序及交换二个数据的正确实现
    用Apache Velocity模板引擎速造网站
    同一份代码打成多种包(JAR/WAR/EAR)时,如果不用classifier会产生什么有趣效果?
  • 原文地址:https://www.cnblogs.com/romaLzhih/p/9489797.html
Copyright © 2011-2022 走看看