hdu1081 DP类最长子段和(二维压缩+前缀和数组/树状数组计数)

题意:给出一个 n * n 的数字矩阵,问最大子矩阵和是多少。

由于和最长子段和问题类似,一开始想到的就是 DP ,一开始我准备用两个循环进行 DP ,对于每一个 (i,j) ,考察(i - 1,j)与(i,j - 1), dp 值代表以该点为右下角的最大矩阵和,同时记录下这个矩阵的左上角坐标,状态转移时通过将原和最大矩阵通过补边推到当前和最大矩阵。但是其实这种做法有一个明显的问题,就是转移时,补上边后 dp 值相同怎么办,dp 值相同而矩阵不同的话会影响到下一次状态转移后补上的矩阵的情况,从而影响到下一个矩阵的判断。

并想不出怎么做的我无奈看了题解……二维压缩,好吧,并不懂那是个什么……细看之下才知道,其实就是用数组记录下矩阵后,在 dp 时对于起始结束行不同的矩阵分别 DP ,记录下其中最大值即可。例如对于所有列,考虑其前两行的情况,即子矩阵行数为 2 ,这时每列的两个数可以计算其和为一个数,就能将二维的矩阵转化为一维的数组了,这样再进行与最长子段和相同的操作就能得出答案了。

当然,记录矩阵并且便于计算行之间的和,我用了前缀和数组和树状数组两种方式。这题明显用前缀和数组更加好,因为输入的数不会发生改变,所以前缀和数组更加容易计算,用树状数组做并不是用来体现我的逼格高,只是因为我个人树状数组基本没有在题目中用过几次,所以这次敲一遍训练一下,以免以后遇到了明明会但是敲不出来或者敲得太慢……说白了就是弱……

前缀和数组:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define max(a,b) a>b?a:b
 4
 5 int t[101][101],dp[101];
 6
 7 int main(){
 8     int n;
 9     while(scanf("%d",&n)!=EOF&&n!=0){
10         int i,j,k,ans=-0xFFFFFFF,tmp;
11         memset(t,0,sizeof(t));
12         for(i=1;i<=n;i++){
13             for(j=1;j<=n;j++){
14                 scanf("%d",&tmp);
15                 t[j][i]=t[j][i-1]+tmp;
16             }
17         }
18         for(i=0;i<=n-1;i++){
19             for(j=i+1;j<=n;j++){
20                 memset(dp,0,sizeof(dp));
21                 for(k=1;k<=n;k++){
22                     if(dp[k-1]>0){
23                         dp[k]=dp[k-1]+(t[k][j]-t[k][i]);
24                     }
25                     else dp[k]=t[k][j]-t[k][i];
26                     ans=max(ans,dp[k]);
27                 }
28             }
29         }
30         printf("%d\n",ans);
31     }
32     return 0;
33 }

树状数组:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define max(a,b) a>b?a:b
 4
 5 int t[101][101],dp[101],n;
 6
 7 void add(int i,int j,int d){
 8     while(j<=n){
 9         t[i][j]+=d;
10         j+=(j&-j);
11     }
12     return;
13 }
14
15 int sum(int k,int x){
16     int cnt=0;
17     while(x>0){
18         cnt+=t[k][x];
19         x-=(x&(-x));
20     }
21     return cnt;
22 }
23
24 int main(){
25     while(scanf("%d",&n)!=EOF&&n!=0){
26         int i,j,k,ans=-0xFFFFFFF,tmp;
27         memset(t,0,sizeof(t));
28         for(i=1;i<=n;i++){
29             for(j=1;j<=n;j++){
30                 scanf("%d",&tmp);
31                 add(i,j,tmp);
32             }
33         }
34         for(i=0;i<=n-1;i++){
35             for(j=i+1;j<=n;j++){
36                 memset(dp,0,sizeof(dp));
37                 for(k=1;k<=n;k++){
38                     tmp=sum(k,j)-sum(k,i);
39                     if(dp[k-1]>0){
40                         dp[k]=dp[k-1]+tmp;
41                     }
42                     else dp[k]=tmp;
43                     ans=max(ans,dp[k]);
44                 }
45             }
46         }
47         printf("%d\n",ans);
48     }
49     return 0;
50 }

时间: 2024-10-10 21:28:07

hdu1081 DP类最长子段和(二维压缩+前缀和数组/树状数组计数)的相关文章

bzoj 1264: [AHOI2006]基因匹配Match (树状数组优化dp)

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1264 思路: n大小为20000*5,而一般的dp求最长公共子序列复杂度是 n*n的,所以我们必须优化. 题目说了一个数会出现5次,那么我们可以预处理得到 第一个序列a[]每个数字分别在哪些位置, 因为求LCS的状态转移方程中当 s1[i-1] == s2[j-1]时,dp[i][j] = dp[i-1][j-1] + 1;只有当两个点相同时 值才会+1,我们可以对第二个序列b[]遍历一遍

Android的二维码功能实现以及长按识别二维码

一.初步集成Zxing项目 二维码的识别可是在生活中随处可见的,现在基本上所有APP都有二维码的相关操作,如果识别二维码从头开始开发做起来还是相当复杂和麻烦的,从零开始开发肯定不现实,最好的做法就是借助现有的开源项目,Github上名气最大的就是Zxing了,提供了多个平台的二维码扫码解决方案,开源项目地址是:https://github.com/zxing/zxing,我们今天集成Zxing到我们自己的项目中去,并实现一些二维码生成.扫描.长按识别二维码等相关操作: 1.下载Zxing项目所依

hdu6078 Wavel Sequence dp+二维树状数组

//#pragma comment(linker, "/STACK:102400000,102400000") /** 题目:hdu6078 Wavel Sequence 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6078 题意:给定a序列和b序列.从a中取一个子序列x(数的位置顺序保持不变),子序列每个数满足a1<a2>a3<a4>a5<a6... 波浪形 从b中取相同长度的子序列y,也满足波浪形. 如果x

POJ 2029 Get Many Persimmon Trees (二维树状数组 or DP)

题意:一个H * W的大矩形,里面的某些格子种有树.现在要你找出一个h * w的小矩形,使得里面树的数量最多,问最多有多少棵树 是二维树状数组基础用法,边输入边更新有树的点,建完树后就可以查询每个(1,1)到(x,y)为对顶点的矩形中共有多少棵柿子树. 算法复杂度 O(H*W*lgH*lgW) 但是由于这题的柿子树一旦确定位置后就没有更新位置,所以不需要用树状数组也可,直接用dp统计每个(1,1)到(x,y)为对顶点的矩形中共有多少棵柿子树. 统计的状态转移方程是: for(int i=1;i<

Matrix 二维树状数组的第二类应用

Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 17976   Accepted: 6737 Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1

spa(单页应用)中,使用history模式时,微信长按识别二维码在ios下失效的问题

spa(单页应用,vue)中,使用history模式时,微信长按识别二维码在ios下失效的问题. 触发条件: spa单页应用: 路由模式 history 从其他页面跳转到带有微信二维码识别的页面(不是直接打开该页面) ios版本的微信(实测版本6.5.19) 结果: 二维码长按无法识别,刷新页面后恢复正常,安卓下正常. 解决方案: 1. 进入该页面的方式不使用路由跳转,而改为 <a href="xxx">目标二维码页面</a>的方式: 2. 在beforeCre

[CTSC2017]最长上升自序列(伪题解)(树状数组+DP套DP+最小费用最大流+Johnson最短路+Yang_Tableau)

部分分做法很多,但每想出来一个也就多5-10分.正解还不会,下面是各种部分分做法: Subtask 1:k=1 LCS长度最长为1,也就是说不存在j>i和a[j]>a[i]同时成立.显然就是一个LDS,树状数组直接求即可. Subtask 2:k=2 最多两个,也就是可以由两个LCS拼起来,f[i][j]表示第一个LCS以i结尾,第二个以j结尾的方案数,转移显然. Subtask 3:k=2 树状数组优化DP,复杂度由$O(n^3)$降为$O(n^2 \log n)$ Subtask 4,5:

[BZOJ3594] [Scoi2014]方伯伯的玉米田 二维树状数组优化dp

我们发现任何最优解都可以是所有拔高的右端点是n,然后如果我们确定了一段序列前缀的结尾和在此之前用过的拔高我们就可以直接取最大值了然后我们在这上面转移就可以了,然后最优解用二维树状数组维护就行了 #include<cstdio> #include<cstring> #include<algorithm> #define N 10005 #define K 505 #define M 5505 using namespace std; inline int read() {

【转】关于LIS和一类可以用树状数组优化的DP 预备知识

原文链接 http://www.cnblogs.com/liu-runda/p/6193690.html 预备知识 DP(Dynamic Programming):一种以无后效性的状态转移为基础的算法,我们可以将其不严谨地先理解为递推.例如斐波那契数列的递推求法可以不严谨地认为是DP.当然DP的状态也可以是二维/三维的,某一维的含义也不仅仅是指某个数列的第几项. 树状数组(BIT or fenwick tree):一种高效地动态维护一个序列并动态求取前缀和的数据结构.修改某个元素/求一次前缀和的