zoukankan      html  css  js  c++  java
  • 线性DP总结(studying

    写在前面

    虽然都说线性DP是入门,但我还是今天才开始学

    线性DP就是珂以通过线性处理得出答案的一种DP

    每一种状态都可以从前面推得,并且推导过程是呈线性的

    参考题单(本人现在主要用luogu,所以这些题都是luogu上找的)

    下面是例题:

    P1057 传球游戏 

    这道题算是热身题吧

    Solution

    思路很简单,

    每个人手中的球只能从他左边的同学和右边的同学传过来,所以递推求就好了

    我们用i表示编号,j表示第几次传球,f[][]表示有几条到达这种状态的“路”

    那么可以推出递推式:

    f[1][0] = 1;

    f[i][j] = f[i-1][j-1] + f[i+1][j-1];

    下面是代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 using namespace std;
     4 int n, m;
     5 int f[33][33];
     6 int main()
     7 {
     8     scanf("%d%d", &n, &m);
     9     f[1][0] = 1;
    10     for(int j = 1; j <= m; ++j){
    11         for(int i = 1; i <= n; ++i){
    12             int x, y;
    13             if(i - 1 == 0) x = n;
    14             else x = i - 1;
    15             if(i + 1 == n + 1) y = 1;
    16             else y = i + 1;
    17             f[i][j] = f[x][j-1] + f[y][j-1];
    18         }
    19     }
    20     printf("%d", f[1][m]);
    21     
    22     return 0;
    23 }
    View Code

    P1233 木棍加工

    简述题意

    如果下次加工的木棍比上一次的又短又细,就不需要准备时间,否则需要一分钟的准备时间

    Solution

    先将木棍按长度从大到小排序,在跑一个最长上升子序列即可(不知道为什么按宽度排序跑出来90pts

    粘代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 const int MAXN = 5010;
     6 struct Wood{
     7     int l,w;
     8     bool operator < (const Wood &b) const {return l > b.l;}
     9 }wood[MAXN];
    10 int n, ans;
    11 int f[MAXN];
    12 int main()
    13 {
    14 //    freopen("P1233_8.in", "r", stdin); 
    15     scanf("%d", &n);
    16     for(int i = 1; i <= n; ++i){
    17         scanf("%d%d", &wood[i].l, &wood[i].w);
    18     }
    19     
    20     sort(wood + 1, wood + 1 + n);
    21     
    22     for(int i = 1; i <= n; ++i){
    23         f[i] = 1;
    24         for(int j = 1; j <= i; ++j){
    25             if(wood[i].w > wood[j].w) f[i] = max(f[i], f[j] + 1);
    26         }
    27         ans = max (ans, f[i]);
    28     //    cout<<ans<<endl;
    29     }
    30     
    31     printf("%d", ans);
    32     
    33     return 0;
    34 }
    View Code

    P1020 导弹拦截

    简述题意

    现有某导弹拦截系统,每一次拦截的导弹只能小于等于上一次拦截的导弹(当然,第一次珂以拦截任意高度的导弹),给你依次飞来的导弹高度,输出最多能拦截的导弹数和拦截所有导弹最少需要多少套系统

    Solution

    因为这个系统拦截的导弹高度是单调不升的,所以第一问就是一个最长不上升子序列

    第二问珂以根据Dilworth定理推得求一个最长上升子序列

    某大佬JJPJJ对于求最长上升子序列的证明:

    (1)假设打导弹的方法是这样的:取任意一个导弹,从这个导弹开始将能打的导弹全部打完。而这些导弹全部记为为同一组,再在没打下来的导弹中任选一个重复上述步骤,直到打完所有导弹。

    (2)假设我们得到了最小划分的K组导弹,从第a(1<=a<=K)组导弹中任取一个导弹,必定可以从a+1组中找到一个导弹的高度比这个导弹高(因为假如找不到,那么它就是比a+1组中任意一个导更高,在打第a组时应该会把a+1组所有导弹一起打下而不是另归为第a+1组),同样从a+1组到a+2组也是如此。那么就可以从前往后在每一组导弹中找一个更高的连起来,连成一条上升子序列,其长度即为K;

    (3)设最长上升子序列长度为P,则有K<=P;又因为最长上升子序列中任意两个不在同一组内(否则不满足单调不升),则有

    P>=K,所以K=P。

    直接暴力

     1 /*
     2 Work by: Suzt_ilymics
     3 Knowledge: 线性DP 
     4 */
     5 #include<iostream>
     6 #include<cstdio>
     7 using namespace std;
     8 const int MAXN = 1e5+4;
     9 int n, ans = -1, cnt = -1;
    10 int a[MAXN];
    11 int f[MAXN], sum[MAXN];
    12 
    13 int max(int x, int y){return x > y ? x : y; }
    14 
    15 int main()
    16 {
    17     while(cin >> a[++n]);
    18     for(int i = 1; i < n; ++i){
    19         f[i] = 1;
    20         sum[i] = 1;
    21         for(int j = 1; j < i; ++j){
    22             if(a[j] >= a[i])
    23             f[i] = max(f[i], f[j] + 1);
    24             if(a[j] < a[i])
    25             sum[i] = max(sum[i], sum[j] + 1);
    26         }
    27         ans = max(ans, f[i]);
    28         cnt = max(cnt, sum[i]);
    29     }
    30     
    31     printf("%d
    %d", ans, cnt);
    32     
    33     return 0;
    34 }
    O(n^2)代码

    显然会T

    所以这里用到了某大佬「已注销」的常数优化方法:

    在朴素n^2算法中,用f[i]储存以i结尾的最长不上升子序列长度,如样例

    i 1 2 3 4 5 6 7 8

    a 389 207 155 300 299 170 158 65

    f 1 2 3 2 3 4 5 6

    发现当f的值相同时,越后面的导弹高度越高

    用d[i]维护f值为i的最后一个导弹的位置,t记录当前已经求出最长不升子序列长度

    递推求f时枚举a[d[t]],a[d[t-1]],。。。,a[d[1]]是否≥当前求的导弹高度,是就更新f

     这是优化后代码

     1 /*
     2 Work by: Suzt_ilymics
     3 Knowledge: 线性DP, LIS 
     4 Time: O(??)
     5 */
     6 #include<iostream>
     7 #include<cstdio>
     8 using namespace std;
     9 const int MAXN = 1e5+4;
    10 int n, ans = -1, cnt = -1;
    11 int a[MAXN], d[MAXN], t;//用d数组维护f值为i的最后一个导弹的位置 
    12 int f[MAXN], sum[MAXN];
    13 
    14 int max(int x, int y){return x > y ? x : y; }
    15 
    16 int main()
    17 {
    18     while(cin >> a[++n]);
    19     //第一问:求最长不上升子序列 
    20     for(int i = 1; i < n; ++i){
    21         f[i] = 1;
    22         for(int j = ans; j > 0; --j){
    23             if(a[i] <= a[d[j]]){
    24                 f[i] = f[d[j]] + 1; break;
    25             }
    26         } 
    27         d[f[i]] = i;
    28         ans = max(ans, f[i]);
    29     }
    30     
    31     //第二问,求最长上升子序列 
    32     for(int i = 1; i < n; ++i){
    33         sum[i] = 1;
    34         for(int j = cnt; j > 0; --j){
    35             if(a[i] > a[d[j]]){
    36                 sum[i] = sum[d[j]] + 1; break;
    37             }
    38         }
    39         d[sum[i]] = i;
    40         cnt = max(cnt, sum[i]);
    41     }
    42     
    43     printf("%d
    %d", ans, cnt);
    44     
    45     return 0;
    46 }
    O(??)代码

     本人做完这个题突发灵感,写了一道题目这里,希望有大佬能提出具体思路帮我完善题面

  • 相关阅读:
    python深浅拷贝
    软件开发目录规范
    编码规范
    python进程、线程、协程的介绍及使用
    soket粘包问题及解决方案
    python socket通信
    数据开发_机器学习
    数据开发_开发工具以及工具链
    数据开发_Python读取文件
    数据开发_Java设计模式_IO以及读取资源文件
  • 原文地址:https://www.cnblogs.com/Silymtics/p/13828964.html
Copyright © 2011-2022 走看看