zoukankan      html  css  js  c++  java
  • 动态规划大合集II

    1、前言

            大合集总共14道题,出自江哥之手(这就没什么好戏了),做得让人花枝乱颤。虽说大部分是NOIP难度,也有简单的几道题目,但是还是做的很辛苦,有几道题几乎没思路,下面一道道边看边分析一下。

     

    2、lis 最长上升子序列

            唯一一道裸题,但是O(n^2)过不了,临时看了看O(n log n)的二分做法和线段树做法。先来讲讲简单的二分做法,其本质就是在O(n^2)上进行优化,需要证明一个结论。设当前处理数列第k位,存在:

      (1)a[i]<a[j]<a[k];

      (2)i<j<k;

      (3)f[i]=f[j]。

      此时我们选择a[i]好还是a[j]好?显然是a[i]好。为什么?倘若存在a[i]<a[x]<a[j],i<x<j,则选择a[i]可以得到更长的子序列——a[i],a[x],a[k]……这样,对于f[t]=k,我们只需要保存a[t]值最小的那个t了,可以证明得f数组将会单调递增,故我们每次不需要向前每一位都进行查找,而可以进行二分查找,故时间复杂度为O(n log n)。

    ------------------------------------------------------------------------------------------------------

    #include<cstdio>

    int min(int a,int b) { return (a<b)?a:b; }

    int v[200010],n,x,ans;

    int main()
    {
      freopen("lis.in","r",stdin);
      freopen("lis.out","w",stdout);
      scanf("%d", &n);
      for (int i=1;i<=n;i++)
      {
        scanf("%d",&x);
        int l=1,r=ans,mid=(l+r)>>1,loc=0;
        while (l<=r)
        {
          if (v[mid]<=x) loc=mid,l=mid+1;
          else r=mid-1;
          mid=(l+r)>>1;
        }
        if (loc==ans) v[++ans]=x;
        else v[loc+1]=min(v[loc+1],x);
      }
      printf("%d ",ans);

    }

    ------------------------------------------------------------------------------------------------------

    3、chess 黑白棋盘

            根据题目分析,我们需要维护该列是否存在棋子;该行是否存在棋子;由于n<=15,我们可以考虑二进制状态压缩进行转移,最多有2^(n+1)种状态。设f[i][j]为当前第i行,状态为j(压缩的二进制状态 )时的方案数。今天才刚刚学会用&、^、|等位运算符,上次写状压的时候还是手写的。。。方程:f[i][j]=sum{f[i-1][j^(1<<(k-1))]} (k∈[1,n],j&(1<<(k-1)),!a[i][k])

    代码:

    ------------------------------------------------------------------------------------------------------

    #include <cstdio>
    #define MOD 1e9+7

    int n,a[20][20],f[20][100000],ans;
    char ch;

    int main()
    {
      freopen("chess.in","r",stdin);
      freopen("chess.out","w",stdout);
      scanf("%d",&n);
      for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
          do ch=getchar(); while ((ch!='0')&&(ch!='1'));
          a[i][j]=ch-'0';
        }
      f[0][0]=1;
      for (int i=1;i<=n;i++)
        for (int j=0;j<1<<n;j++)
        {
          f[i][j]=f[i-1][j];
          for (int k=1;k<=n;k++)
            if ((j&(1<<(k-1))) && (!a[i][k])) 
            {
              f[i][j]+=f[i-1][j^(1<<(k-1))];
              if (f[i][j]>=MOD) f[i][j]-=MOD;
            }
        }
      for (int i=0;i<1<<n;i++) { ans+=f[n][i]; if (ans>=MOD) ans-=MOD; }
      printf("%d",ans);
      return 0;
    }

    ------------------------------------------------------------------------------------------------------

    4、bound 多背包问题

    背包问题,但是需要用到单调队列优化。题解有点没懂?但是看代码大概清楚了。

    ------------------------------------------------------------------------------------------------------

    #include<cstdio>
    #include<algorithm>
    #define MAXN 10005
    using namespace std;

    int q[MAXN],f[MAXN],g[MAXN];
    int n,m,s,w,v;

    int dp(int x,int y) { return g[x]+(y-x)/w*v; }

    int main()
    {
      freopen("bound.in","r",stdin);
      freopen("bound.out","w",stdout);
      scanf("%d %d",&n,&m);
      for (int i=1;i<=n;i++)
      {
        scanf("%d %d %d",&s,&w,&v);
        for (int start=0;start<w;start++)
        {
          int tail=0,head=1;
          for (int j=start;j<=m;j+=w)
          {
            while (head<=tail && (j-q[head])/w>s) ++head;
            while (head<=tail && dp(q[tail],j)<=dp(j,j)) --tail;
            q[++tail]=j;
            f[j]=dp(q[head],j);
          }
        }
        swap(f,g);
      }
      int ans=0;
      for (int i=0;i<=m;i++) ans=max(ans,g[i]);
      printf("%d",ans);
      return 0;
    }

    ------------------------------------------------------------------------------------------------------

     

    5、matrix 子矩阵

            也是所有题目中最需要码力的了吧,首先步骤比较复杂,情况比较繁琐,但是没有什么需要优化的地方。由于必定存在某条直线将三个矩阵割开,主要思路为枚举切割线,然后就只需要考虑两个矩阵的情况了。

    6、windy windy数

            数位动态规划,考试时想出来了但是没写下去。由于我们所需的信息明显:当前确定的数是否小于原数的这些位所对应的数;最后一位数;是否存在前导0,所以用记忆化搜索可能更易理解。下面代码来自yxj。

    ------------------------------------------------------------------------------------------------------

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;

    int p[20], r[20];
    int f[20][20][2][2];
    int a, b;

    int dfs(int pos, int last, int s, int zero) {
      if (pos > 10) return s == 0 && zero == 0;
      int &u = f[pos][last][s][zero];
      if (u != -1) return u;
      u = zero == 0 && (r[pos] || s == 0);
      for (int i = 0; i <= 9; ++i) {
        if (abs(last - i) < 2) continue;
        int ns = i > p[pos] ? 1 : (i == p[pos] ? s : 0);
        u += dfs(pos + 1, i, ns, i == 0);
      }
      return u;
    }


    int tot(int x) {
      for (int i = 0; i <= 10; ++i) {
        p[i] = x % 10;
        r[i] = x;
        x /= 10;
      }
      memset(f, -1, sizeof(f));
      return dfs(0, 11, 0, 0);
    }


    int main() {
      freopen("windy.in", "r", stdin);
      freopen("windy.out", "w", stdout);

      cin >> a >> b;
      cout << tot(b) - tot(a - 1) << endl;
    }

    ------------------------------------------------------------------------------------------------------

  • 相关阅读:
    JS流程控制语句
    JS操作符
    JS数据类型
    JS基础语法与JS变量
    CSS案例1(导航栏)
    CSS三大特性
    CSS——背景,渐变和列表
    HTML+CSS项目——模拟京东网页
    JavaScript简介
    js -04课程 更换背景图片
  • 原文地址:https://www.cnblogs.com/jinkun113/p/4709397.html
Copyright © 2011-2022 走看看