• 动态规划


    线性动态规划

    洛谷P2285 打鼹鼠

    把鼹鼠按照出现的时间排成一列,因为必须先打早些出现的鼹鼠再打晚些出现的鼹鼠,所以在打当前鼹鼠时要先确定上一个打的鼹鼠是谁,打到他时一共最多打了多少鼹鼠,这种思路就和LIS很像了,一点不同就在于并不是所有t[i]<t[j]都可以先打i再打j,必须满足两鼹鼠的曼哈顿距离小于等于他们的出现时间差

    洛谷P1868 饥饿的奶牛
    a[i][0]表示i位置是否为某一区间的起点
    a[i][1]表示以i为起点的区间的价值
    f[i]表示到i位置的最大价值是多少
    因为不允许区间重叠,所以一旦选了某个区间,其他的价值只能来自这个区间之外,考虑从前往后进行状态转移,那么f[i+a[i][1]]=max{f[i]+a[i][1]} 其中i必须保证是区间i的起点

    P3004 [USACO10DEC]宝箱Treasure Chest

    区间dp
    f[i][j]表示在i,j这段区间内先手能获得的最大分数;那么后手在先手最优方案走法下,按最优方案走的最大分数就是i,j这个区间总分数减去f[i][j].

    洛谷P3146 [USACO16OPEN]248

    区间dp,f[i][j]表示区间 i-j 合并的最大值
    可以由f[i][k],f[k+1][j]转移过来,转移条件为f[i][k]==f[k+1][j]

    背包型

    /*
        for(int i=1;i<=n;i++)
            for(int j=m;j>=a[i];j--)
                f[j]+=f[j-a[i]];
        这样吗
        为什么这样m元钱就是刚好花完的
        
        从m开始枚举的
        枚举了所有的状态
        所以从1到m都会有
        
        如果m花不完或者凑不出m
        那么m的前继状态的方案数一定是0
        无论怎样f[m]+=f[m-w[i]]
        f[m]都是0
        但是如果可以拼出
        刚才的枚举就会把方案数转移出
        因为可以从n层枚举摘取这样的情况
        f[0]=1,w[1]=1,w[2]=2,m=3
        
        f[1]+=f[1-w[1]]
        f[3]+=f[1-w[2]]
        那么f[m]就能从f[1]转移过来
        f[1]就能从f[0]转移过来
    */
    #include<iostream>
    #include<cstdio>
    using namespace std;
    int f[10000],n,m,a[110];
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        f[0]=1;
        for(int i=1;i<=n;i++)
            for(int j=m;j>=a[i];j--)
                f[j]+=f[j-a[i]];
        printf("%d",f[m]);
    }
    01背包求方案数
    /*
        输入x,y,z表示第i个物品的重量为x,价值为y,属于z组
    */
    #include<iostream>
    #include<cstdio>
    #define maxn 1010
    using namespace std;
    int n,m,f[maxn],a[maxn][maxn],v[maxn],w[maxn],t;
    int main(){
        scanf("%d%d",&m,&n);
        int x,y,z;
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&w[i],&v[i],&z);
            a[z][++a[z][0]]=i;t=max(t,z);
        }
        for(int k=1;k<=t;k++)
            for(int j=m;j>=0;j--)
                for(int i=1;i<=a[k][0];i++){
                    if(j-w[a[k][i]]>=0)
                    f[j]=max(f[j],f[j-w[a[k][i]]]+v[a[k][i]]);
                }
        printf("%d",f[m]);
    }
    分组背包

    洛谷P2854 [USACO06DEC]牛的过山车Cow Roller Coaster
    看到本题不难想到二维费用的背包f[i][j]=max{f[i-a[i].len][j-a[i].w]+a[i].v},但是题目要求所有铁轨首尾相连,所以需要对a[]按起点排个序,剩下的就和上面的方程很像了

    棋盘型

    洛谷P2380 狗哥采矿

    我们定义f[i][j]f[i][j]为在以(i,j)(i,j)为右下角的子矩阵中的最大采矿量,由题意我们可知,如果(i,j)(i,j)是向左转移矿,那么(i,j-1)(i,j?1),一定也是向左,(i,j-2)(i,j?2)一直到(i,1)(i,1)都是向左,同理如果(i,j)(i,j)是向上转移矿,那么(i-1,j)(i?1,j),一定也是向上,(i-2,j)(i?2,j)一直到(1,j)(1,j)都是向左。这就可以其实我们用前缀和去维护一段区间的采矿量。
    在转移时,我们只关心当前(i,j)(i,j)的采矿方向。设A[i][j]A[i][j]为向上的前缀和,B[i][j]B[i][j]为向左的前缀和,那么转移方程f[i][j]=max(f[i-1][j]+B[i][j],f[[i][j-1]+A[i][j])f[i][j]=max(f[i?1][j]+B[i][j],f[[i][j?1]+A[i][j]).

    像这种要连续选择的问题可以考虑用前缀和维护

    概率dp

    poj3071 Football

    乘法原理,设f[j][i]表示第j支球队通过第i场比赛的概率,则:f[j][i]=sum(f[j][i-1]*f[j+k][i-1]*p[j][j+k]),其中j+k是它这一场可能面对的对手,实际上就是它上一场比赛的第一支队伍加2^(i-1)一直加到2^1 

    数位dp

    反击数

    状压dp

    洛谷P3070 [USACO13JAN]岛游记Island Travels

    综合来说这真的是一个好题,但是dp部分还是比较基础的

    因为题目要求以连通块为单位,所以就先求出所有'X'所在的连通块,flag[i][j]就是i,j位置上的点所在的联通块,num[i]是编号为i的连通块的大小,然后以每个连通块为起点进行spfa,经过所有spfa后可以得出任意两连通块之间的最短路。就可以开始dp啦!
    dp[S][j]表示走过点集S到达j的最短路径,转移:dp[S/j][k] + dis[k,j]

    洛谷P2831 愤怒的小鸟

    数据范围很小,考虑状态压缩,把打死的猪的集合压缩成二进制

    g[i][j]:打死i,j两头猪的抛物线能打到的猪的集合 
    f[s]:被打死的猪的集合为s时最少打多少次 
    for(int i=1;i<(1<<n);i++){
        int j=0;
        while((i&(1<<j))==0)j++;
        f[i]=f[i^(1<<j)]+1;
        for(int k=0;k<n;k++)
            if(((1<<k)&i)&&k!=j)
                f[i]=min(f[i],f[(i&g[j][k])^i]+1);
    }

     洛谷P3052 [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper

    f 数组为结构体

    f[S].cnt 表示集合 S 最少的分组数

    f[S].v 表示集合 S 最少分组数下当前组所用的最少容量

    f[S] = min(f[S], f[S - i] + a[i]) (i ∈ S)

    运算重载一下即可。

     洛谷P2915 [USACO08NOV]奶牛混合起来Mixed Up Cows

    比较简单的一个题

    f[sta][i]表示选了的牛的集合为sta,且最后一个牛是i的混乱序列方案数,正着推就行

    if(sta&(1<<(i-1)))
        for(int j=1;j<=n;j++)
            if(abs(a[j]-a[i])>k)f[sta][i]+=f[sta^(1<<(i-1))][j];

     洛谷P3092 [USACO13NOV]没有找零No Change

    因为是顺序购买,所以状态s的答案有关的是使用顺序。之后就有状态f[s]表示用状态s最多可买多少物品。最后在算状态最大值的时候要套一个二分。

    动规求方案数

    洛谷P1310 表达式的值

    注意判断分类和分步,分类加法,分步乘法。这种表达式上的问题多要用到栈,而对于括号里的计算多用递归就求解

    这道题可以用DP求解,设f(s,0)为s=0的方案数,f(s,1)为s为1的方案数,则
    f(a+b,0)=f(a,0)*f(b,0);
    f(a+b,1)=f(a,0)*f(b,1)+f(a,1)*f(b,0)+f(a,1)*f(b,1);
    f(a*b,0)=f(a,0)*f(b,0)+f(a,1)*f(b,0)+f(a,0)*f(b,1);
    f(a*b,1)=f(a,1)*f(b,1)。
    接下来就是一个类似于树形DP的过程了。在这里DP的是表达式树。

    在dp中有个很著名的恒等变换(手动滑稽),就是 求最值=判断最值的可行性
  • 相关阅读:
    hdu 3415 Max Sum of MaxKsubsequence
    poj 2243 Knight Moves
    【LCD手册】飞凌3.5/4.3/5.6/7/8 寸LCD手册大全下载
    【转载zq2002好文章】Windows CE 休眠唤醒全面解析(基于2440平台)(1)(2)(3)
    【转载】如果做到窗口自动适应不同尺寸屏幕的尺寸?
    【转载】wince6.0+s3c6410摄像头驱动修改
    推荐一个比较活跃的ARM/WinCE/LinuxQQ群
    【转载】微软的USB摄像头驱动,
    【收集】ARM+WinCE QQ 群
    【转载】S3C6410移植日记系列
  • 原文地址:https://www.cnblogs.com/thmyl/p/7568751.html
Copyright © 2020-2023  润新知