zoukankan      html  css  js  c++  java
  • 寒假集训Day3

    今天袁老师给我们讲了几个经典DP模型

    如下

    1.合并石子

    题意:有n堆石子,两堆相邻的石子可以堆在一起,最终要使得这n堆石子合并成一堆,新堆的石头数记为本次合并的得分,最小的得分

    袁老师首先用递推给我们分析了方案总数的求和Catalan数

    然后告诉了我们递推=统计 动规=选择 的区别

    开始分析动规做法

    首先,

    输入每堆石子数,初始化:s[i]=s[i-1]+本次石子数

    i from 1 to n

    将f[i][i]初始化为0,(因为一开始只知道f[i][i]的初值)

    倒着来,把一堆石子每次分成两堆,k的位置就是分界点

    s[i]表示前i堆石头的数量总和,f[i][j]表示把第i堆石头到第j堆的石头合并成一堆的最优值(这里最优后面也会最优,因此满足最优子结构性质

    然后,核心代码:

    for(int i=n-1;i>=1;i++)
      for(int j=i+1;j<=n;j++)
        for(k=i;j<=j-1;k++)
        f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1])
    输出f[1][n]

     f[i][j]=min{f[i][k]+f[k+1][j]}+sum[j-1]+sum[i-1](从i到j石子量)

    for(i=1~n-1)

    for(j=i+1~n)

    for(k=i~j-1)

    i为左端点

    j为右端点

    2.乘积最大

    题意:给定两个整数n和m,表示一个长度为n的数字串,你可以在其中插入m个乘号

    一开始袁老师“随便”举出了一个数字“365940857”,让我们知道了老师的qq号

    咳咳咳,我什么都没说,老师又拿了一个乘号告诉我们当把这个数字劈开两半时,有一半还有一次劈的机会,有两种可能:1.左边有机会,右边无 2.右边有机会,左边无

    总而言之用k来确定劈的位置,使得两边最优,再选择最优边再劈,以此类推(这是区间dp)

    正解如下:

    初始化:f[i][0]=前i个数本身

    j from 1 to m

    i from j+1 to n

    k from j to i-1

    f[i][j]表示前i个数插j个乘号,在k和k+1中间加乘号,则有f[i][j]=max(f[i][j],f[k][j-1]*a[k+1][i])

    输出f[n][m]

    3.机器分配

    i个公司分配j台机器
    f[i][j]=f[i-1][j-k](分配了k台,前i-1个公司还剩j-k台)+a[i][k]
    k不超过j

    4.挖地雷

    用邻接矩阵保存每个地窖到另一个地窖,二维数组[i][j]表示从i到j是否有通路,有是1,没有是0

    枚举每一个其他地窖到这个地窖的路径,能走就加上地雷数

     1 #include <bits/stdc++.h>
     2 int n,maxn=0;
     3 int main(){
     4     int bomb[20+5],road[20+5][20+5],f[20+5],c[20+5];
     5     memset(road,0,sizeof(road));
     6     memset(f,0,sizeof(f));
     7     memset(c,0,sizeof(c));
     8     scanf("%d",&n);
     9     for(int i=1;i<=n;i++){
    10         scanf("%d",&bomb[i]);
    11     }
    12     for(int i=1;i<=n-1;i++){
    13         for(int j=i+1;j<=n;j++){
    14             scanf("%d",&road[i][j]);
    15         }
    16     }
    17     f[n]=bomb[n];
    18     int l=0,k=0;
    19     for(int i=n-1;i>=1;i--){
    20         l=0;
    21         k=0;
    22         for(int j=i+1;j<=n;j++){
    23             if(road[i][j]==1&&f[j]>l){
    24                 l=f[j];
    25                 k=j;
    26             }
    27         }
    28             f[i]=l+bomb[i];
    29             c[i]=k;
    30     }
    31     k=1;
    32     for(int i=2;i<=n;i++){
    33         if(f[i]>f[k]){
    34             k=i;
    35         }
    36     }
    37     maxn=f[k];
    38     printf("%d",k);
    39     k=c[k];
    40     while(k!=0){
    41         printf(" %d",k);
    42         k=c[k];
    43     }
    44     printf("
    %d",maxn);
    45     return 0;
    46 }
    挖地雷

    5.友好城市

    从1~n将北岸城市一一编号,一一对应将南岸城市编号,两岸对应连线,求两岸城市编号最长公共子序列

    6.橱窗布置

    有f束花,v个花瓶

    初始化:

    1.我们知道f[0][0]一定是0

    2.f[1][j]=a[1][j](a数组表示前i束花插到前j个花瓶里的美学价值)(j from 1 到 v-f+1)

    所以只需让i从2到f

    j从i到v-f+i循环

    f[i][j]表示第i束花插到第j个花瓶里

    f[i][j]=f[i-1][k]+a[i][j](考虑最后一束花插到第k个花瓶里,因为不知道k是多少,所以循环k=i-1 to j-1

    7.方格取数

    不管怎样,都走n+m-2步

    假设两个人走,取两人走的最大值

    f[i][j][k][l]表示第一个人在i行j列第二个人在k行l列

    i,j,k,l均从1 to n

    f[i][j][k][l]=max(f[i-1][j][k-1][l],f[i][j-1][k-1][l],f[i-1][j][k][l-1],f[i-1][j][k-1][l])(不能越界)

    取最大值加a[i][j]

    if(i!=k&&j!=l)

     f[i][j][k][l]+=a[k][l]

     输出f[n][n][n][n]

    需思考:机器分配

    That‘s all
  • 相关阅读:
    第6周编程题:零基础学Java
    帆软报表软件学习计划
    北大软件工程——第八周:面向对象设计2
    hdu1264 Counting Squares
    hdu1264 Counting Squares
    poj1151 Atlantis(线段树+扫描线)
    poj1151 Atlantis(线段树+扫描线)
    bzoj4653 [Noi2016]区间
    bzoj4653 [Noi2016]区间
    Tyvj1043
  • 原文地址:https://www.cnblogs.com/friction/p/8409334.html
Copyright © 2011-2022 走看看