每日三只DP

今天的第一只dp是带树状数组or线段树优化的最大不下降子段和。
现场在Div2被教做人……
然后现学现卖把longlong炸了就滚粗div2了……
改了一改之后结局还是比较美满的。
第一个DP代码见我的胡言乱语篇……《dp不收flag系列》
然后第二个dp慌乱地做了这样一道题:
给定一个矩阵,有些地方是0,有些地方是1,求一个最大的正方形矩阵使得里面全是1。
那么我们设f[i][j] 表示以(i,j)为右下角的正方形的最大边长,则有f[i][j] = min{f[i - 1][j - 1] + 1 ,up[i][j],left[i][j]};
up和left代表从起点往这个方向走最多能经过几个点,即为最大以这个点为右下角顶点的正方形边长。
代码:

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define v edge[i].to
using namespace std;
const int inf = 1 << 30;
int read(){
    char ch = getchar();
    while(ch < '0' || ch > '9')ch = getchar ();
    int x = 0;
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
    return x;
}
int f[1005][1005]; 
bool w[1005][1005];
int up[1005][1005],lft[1005][1005];
int main()
{
    int n = read(),m = read();
    Rep(i,n)
        Rep(j,m){
            w[i][j] = read();
            if(w[i][j])lft[i][j] = lft[i][j - 1] + 1;
            else lft[i][j] = 0;
        }
    Rep(i,m)
        Rep(j,n){
            if(w[j][i])up[j][i] = up[j - 1][i] + 1;
            else up[j][i] = 0;
        }
/*  Rep(i,n)
        Rep(j,m)
            printf("(%d,%d) %d %d\n",i,j,up[i][j],lft[i][j]);*/
    CLR(f,127);
    f[1][1] = w[1][1];
    int ans = 0;
    Rep(i,n)
        Rep(j,m){
            if(w[i][j]){
                f[i][j] = min(f[i - 1][j - 1] + 1,min(up[i][j],lft[i][j]));
                ans = max(ans,f[i][j]);
            }
            else f[i][j] = 0;
        }
    printf("%d\n",ans);
    return 0;
}

所以第三道题决定刷个水题:
对于序列A = 『a1 to an』,定义d(A) =(http://pic002.cnblogs.com/images/2012/305173/2012062923105331.png);
求出d(A);也就是最大2个连续字段和。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define CLR(a,b) memset(a,b,sizeof(a))
#define Rep(i,n) for(int i = 1 ; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x]; ~ i ; i = edge[i].next)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define Rep_d(i,n) for(int i = n; i >= 1; i --)
#define u t[x] 
#define o t[y]
#define lc ch[0]
#define rc ch[1]
#define lson u.lc,l,mid
#define rson u.rc,mid + 1,r
#define N 50005
int l[N],r[N],a[N],n,lMax[N],rMax[N];
int main (){
    int cases;
    scanf("%d",&cases);
    while(cases --){
        CLR(lMax,-127),CLR(rMax,-127);
        scanf("%d",&n);
        Rep(i,n)
            scanf("%d",&a[i]);
        Rep(i,n)
            l[i] = std :: max(l[i - 1] + a[i],a[i]);
        Rep_d(i,n)
            r[i] = std :: max(r[i + 1] + a[i],a[i]);
        Rep(i,n)
            lMax[i] = std :: max(lMax[i - 1],l[i]);
        Rep_d(i,n)
            rMax[i] = std :: max(rMax[i + 1],r[i]);
        int Maxn = - (1 << 30);
        Rep(i,n)
            Maxn = std :: max(Maxn,lMax[i] + rMax[i + 1]);
        printf("%d\n",Maxn);
    }
    return 0;

}

大概就是计算出以x为分界点的左右两边能取得的最大值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值