一个O(N2)的 LCIS DP
f [i] [j] 为 A的前i个元素和B的前J个元素的LCIS。
O(N3) 做法:
此时的代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | for(i=1;i<=n;i++)for(j=1;j<=m;j++)
 {
 if(a[i]==b[j])
 {
 for(k=0;k<j;k++)
 if(a[i]>b[k]&&f[i][j]<f[i-1][k]+1) f[i][j]=f[i-1][k]+1;
 }
 else f[i][j]=f[i-1][j];
 }
 
 | 
优化思路
发现f[i−1][k] 被 重复访问了多次。
因此我们的思路是让k=[1,j),每个k只访问一次
因此我们可以找到之前最大的f[i−1][k]
令它为mx
接下来只需要判断A[i]是否比B[j]大,如果大了,就尝试更新 mx=max(mx,f[i−1][j])
如果$A[i]==B[j] 那么 f[i] [j]=max(f [i] [j] ,mx+1)$
因为f[i][j]是由 $f[i-1] [k] 和 f[i-1] [j] 得到的,但是k已经被压缩成了每个j循环只做一次,因此时间复杂度变成了O(N^2)$
因此我们得到了以下代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | for(i=1;i<=n;i++){
 int mx=0;
 for(j=1;j<=m;j++)
 {
 f[i][j]=f[i-1][j];
 if(a[i]==b[j]) f[i][j]=max(f[i][j],mx+1);
 else if(a[i]>b[j]) mx=max(mx,f[i-1][j]);
 ans=max(ans,f[i][j]);
 }
 }
 
 | 
记住$ mx $每次都要 清空
完整代码如下
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 
 | #include<iostream>#include<cstring>
 #include<cmath>
 #include<cstdio>
 #include<queue>
 #include<stack>
 #include<vector>
 #include<map>
 #include<set>
 #include<algorithm>
 
 using namespace std;
 
 int f[1100][1100];
 int n,m;
 int a[1100],b[1100];
 int t,ans;
 
 int main()
 {
 ios::sync_with_stdio(false);
 int i,j;
 cin>>t;
 for(t;t>=1;t--)
 {
 memset(f,0,sizeof(f));
 ans=0;
 cin>>n;
 for(i=1;i<=n;i++) cin>>a[i];
 cin>>m;
 for(i=1;i<=m;i++) cin>>b[i];
 for(i=1;i<=n;i++)
 {
 int mx=0;
 for(j=1;j<=m;j++)
 {
 f[i][j]=f[i-1][j];
 if(a[i]==b[j]) f[i][j]=max(f[i][j],mx+1);
 else if(a[i]>b[j]) mx=max(mx,f[i-1][j]);
 ans=max(ans,f[i][j]);
 }
 }
 cout<<ans<<endl;
 }
 return 0;
 }
 
 | 
一个n^4 或者 n^3 的 dp
已知两个状态 可以从左边或者右边传值给自己
f [i] [j] [k] [l] 表示 第一条路 走到 (i,j) 第二条路 走到 (k,l) 的最大好感度
a[i] [j] 表示 (i,j) 这个人的好感度
因为终点给的值是0,所以末态 是 f [n] [m-1] [n-1] [m] 的值
| 12
 3
 4
 5
 6
 
 | for(i=1;i<=n;i++)for(j=1;j<=m;j++)
 for(k=1;k<=n;k++)
 for(l=j+1;l<=m;l++)
 f[i][j][k][l]=max(max(f[i][j-1][k-1][l],f[i][j-1][k][l-1]),max(f[i-1][j][k-1][l],f[i-1][j][k][l-1]))+a[i][j]+a[k][l];
 cout<<f[n][m-1][n-1][m];
 
 | 
貌似还有个n^3的优化,因此每一行每一列的i+j都不一样(参考八皇后问题)
悬线法入门
O(NM) 做法
1.设置 l[i] [j] 表示 (i,j)最左边能拓展到的点。
r[i] [j] 表示 (i,j)最右边能拓展到的点。
up[i] [j] 表示能拓展到的最上面的点。
初始化 l[i] [j] = r[i] [j] = j;
mp[i] [j] 表示地图 (i,j) 的状态 。
up[i] [j] 表示 (i,j) 能向上拓展的最大的高度,把它初始化为1。
然后我们可以轻易的推出预处理公式(我们暂时认为 我们要找最大全是1的正方形)
为了简化问题,我们先来看看矩形
如果左边那个点是1,那么这个点就可以向左边拓展,能拓展的最左边的点=上个点能拓展的最左边的点。
同理,右边也是一样。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | inline void getlr(){
 register int i,j;
 for(i=1;i<=n;i++)
 for(j=2;j<=m;j++)
 if(mp[i][j]==mp[i][j-1]&&mp[i][j]==1) l[i][j]=l[i][j-1];
 for(i=1;i<=n;i++)
 for(j=m-1;j>=1;j--)
 if(mp[i][j]==mp[i][j+1]&&mp[i][j]==1) r[i][j]=r[i][j+1];
 return ;
 }
 
 | 
现在预处理完毕后,我们枚举每个点 (i,j) 能够轻易得到这个点所在的最大矩形宽度a =(r [i] [j] -l [i] [j] +1)
于是横着的问题就解决了,竖着就是 up [i] [j]
因此它的最大面积就是 a*up[i] [j]
那么怎么转移呢?
如果上一个点是 1,这个点也是1的话,那么我们可以得到这个方程
l[i] [j]=max(l[i] [j],l[i-1] [j]), r[i] [j]=min(r[i] [j],r[i-1] [j]),up[i] [j]=up[i-1] [j]+1
别忘了这是个二维矩阵,i可以理解为高度。
于是代码就有了
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 
 | #include<iostream>#include<cstdio>
 #include<cstring>
 #include<cmath>
 #include<queue>
 #include<map>
 #include<set>
 #include<vector>
 #include<stack>
 #include<algorithm>
 
 using namespace std;
 
 int n,m,up[110][110],l[110][110],r[110][110],ans;
 int mp[110][110];
 
 inline void getlr()
 {
 register int i,j;
 for(i=1;i<=n;i++)
 for(j=2;j<=m;j++)
 if(mp[i][j]==mp[i][j-1]&&mp[i][j]==1) l[i][j]=l[i][j-1];
 for(i=1;i<=n;i++)
 for(j=m-1;j>=1;j--)
 if(mp[i][j]==mp[i][j+1]&&mp[i][j]==1) r[i][j]=r[i][j+1];
 return ;
 }
 
 inline void dp()
 {
 register int i,j;
 for(i=2;i<=n;i++)
 for(j=1;j<=m;j++)
 {
 if(mp[i][j]==mp[i-1][j]&&mp[i][j]==1) l[i][j]=max(l[i][j],l[i-1][j]),r[i][j]=min(r[i][j],r[i-1][j]),up[i][j]=up[i-1][j]+1;
 int a,b;
 a=r[i][j]-l[i][j]+1;
 b=min(a,up[i][j]);
 ans=max(ans,b*b);
 }
 return ;
 }
 
 int main()
 {
 int i,j;
 scanf("%d %d",&n,&m);
 for(i=1;i<=n;i++)
 for(j=1;j<=m;j++)
 scanf("%d",&mp[i][j]),l[i][j]=r[i][j]=j,up[i][j]=1;
 getlr();
 dp();
 ans=sqrt(ans);
 printf("%d",ans);
 return 0;
 }
 
 | 
O(S^2) 做法
S为点数量。
当 N M 极大的时候,上面的nm 做法就会TLE
因此我们有必要学习这个做法。
N,M<=30000
一看就知道不是NM的做法。
S^2的做法很怪,但是可以尽量看下去。
做法很生猛,对于每一个点都来和其他的点比较一下,然后找最大的。
首先排序了一次,让每个点的循环(i)只用对另外每一个点判断一次
设置 l,r,h; 这些东西他们都不是数组了
l,r,h意义和原来差不多。
首先按照高度进行排序
然后 for i,j in [1,s]
然后循环的时候j从i断开。
对于高度比第i小的点单独dp,比他大的单独dp
初始状态 r=m,l=0
对于j<i h=n-q[i].x
对于j>i h=q[i].x
然后比较的时候,r必然向左,l必然向右。
所以得到dp方程
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | for(i=1;i<=s;i++){
 int r=m,l=0,h=n-q[i].x;
 for(j=i+1;j<=s;j++)
 {
 if(h*(r-l)<=ans) break;
 ans=max(ans,(q[j].x-q[i].x)*(r-l));
 if(q[i].y==q[j].y) break;
 if(q[j].y>q[i].y) r=min(r,q[j].y);
 if(q[j].y<q[i].y) l=max(l,q[j].y);
 }
 r=m,l=0,h=q[i].x;
 for(j=i-1;j>=1;j--)
 {
 if(h*(r-l)<=ans) break;
 ans=max(ans,(q[i].x-q[j].x)*(r-l));
 if(q[i].y==q[j].y) break;
 if(q[j].y>q[i].y) r=min(r,q[j].y);
 if(q[j].y<q[i].y) l=max(l,q[j].y);
 }
 }
 
 | 
完整代码如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 
 | #include<iostream>#include<cstdio>
 #include<cstring>
 #include<cmath>
 #include<queue>
 #include<map>
 #include<set>
 #include<vector>
 #include<stack>
 #include<algorithm>
 
 using namespace std;
 
 int n,m,s;
 
 struct que{
 int x,y;
 }q[10010];
 
 int cmp1(que a,que b)
 {
 if(a.x==b.x) return a.y<b.y;
 return a.x<b.x;
 }
 
 int cmp2(que a,que b)
 {
 if(a.y==b.y) return a.x<b.x;
 return a.y<b.y;
 }
 
 int main()
 {
 int i,j;
 scanf("%d %d",&n,&m);
 scanf("%d",&s);
 for(i=1;i<=s;i++)
 {
 scanf("%d %d",&q[i].x,&q[i].y);
 }
 q[++s].x=0;
 q[++s].x=n,q[s].y=0;
 q[++s].x=0,q[s].y=m;
 q[++s].x=n,q[s].y=m;
 int ans=0;
 sort(q+1,q+s+1,cmp1);
 for(i=1;i<=s;i++)
 {
 int r=m,l=0,h=n-q[i].x;
 for(j=i+1;j<=s;j++)
 {
 if(h*(r-l)<=ans) break;
 ans=max(ans,(q[j].x-q[i].x)*(r-l));
 if(q[i].y==q[j].y) break;
 if(q[j].y>q[i].y) r=min(r,q[j].y);
 if(q[j].y<q[i].y) l=max(l,q[j].y);
 }
 r=m,l=0,h=q[i].x;
 for(j=i-1;j>=1;j--)
 {
 if(h*(r-l)<=ans) break;
 ans=max(ans,(q[i].x-q[j].x)*(r-l));
 if(q[i].y==q[j].y) break;
 if(q[j].y>q[i].y) r=min(r,q[j].y);
 if(q[j].y<q[i].y) l=max(l,q[j].y);
 }
 }
 sort(q+1,q+s+1,cmp2);
 for(i=1;i<s;i++) ans=max(ans,(q[i+1].y-q[i].y)*n);
 printf("%d",ans);
 return 0;
 }
 
 |