返回顶部

坐标DP

坐标DP

题如其名,该类型的DP以坐标的形式出现,也是以坐标为元素进行深搜。

该类型的DP问题还是比较简单的。

一.传纸条

这道题找的是从a[0][0]到a[m][n]的两条不相交的路径,并让这两条路径上的点的值和最大。f[i][j][k][l]表示第一条路走到a[i][j],第二条路走到a[k][l]时好感度和的最大值。
DP时分四种情况,还要注意判断两条路径是否相交。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int f[N][N][N],w[N][N];
int n,m,i,j;
int main(){
	cin>>m>>n;
	for(i=1;i<=m;i++){
		for(j=1;j<=n;j++){
			scanf("%d",&w[i][j]);
		}
	}
	for(int k=1;k<=(n+m-1);k++){
		for(i=1;i<=m;i++){
			for(j=1;j<=m;j++){
				if(k-i+1>0&&k-j+1>0&&k-i+1<=n&&k-j+1<=n){
					int t=f[k][i][j];
					t=max({t,f[k-1][i-1][j],f[k-1][i][j-1],f[k-1][i-1][j-1],f[k-1][i][j]});
					if(i!=j)t+=w[i][k-i+1]+w[j][k-j+1];
					else t+=w[i][k-i+1];
					f[k][i][j]=t;
				}
			}
		}
	}
	cout<<f[n+m-1][m][m];
	return 0;
} 

2.盖房子

该题有两种方法,一种是常规的坐标DP,另一种是不那么常规的二分答案(也是我自己想出来的“歪门邪道”)

法一:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a,b,c[2002][2002],f[2002][2002],ans;
int main(){
    scanf("%d%d",&a,&b);
    for(int i=1;i<=a;i++){
        for(int j=1;j<=b;j++){
            scanf("%d",&c[i][j]);
        }
    }
    for(int i=1;i<=a;i++){
        for(int j=1;j<=b;j++){
            if(c[i][j]==1){
               f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;
               ans=max(ans,f[i][j]);
            }
        }
    }
    printf("%d",ans);
    return 0;
}

常规DP,每一个为1的方块都自成一个正方形,若一个方块的左上,左,上的三个方块都是1,则该方块可以变成2,(代表该方块为右下角的正方形的边长(此处因人而异,看怎么定义位置了,核心就是用一个角上的方块代表整个正方形)),推广一下,会发现,一个方块可以变成它左上、左、上三个位置方块代表数的最小值+1。

可推出状态转移方程: f[i][j]=min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1

还要注意遍历顺序,要是选左下角那个方块为代表,就向右上遍历,总之就是向相反方向遍历,这样一遍就可以得出答案。

法二:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1001;
int g[N][N];
int i,j,n,len,o,m,ans,l,r;
bool check(int x,int y,int l){
    for(int i=x;i<=x+l-1;i++){
        for(int j=y;j<=y+l-1;j++){
            if(!g[i][j])return false;
        }
   }
   o=1;
   return true;
}
int main(){
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            scanf("%d",&g[i][j]);
        }
    }
    r=min(m,n);
    while(l<=r){
          int mid=(l+r)>>1;
          for(i=1;i<=n-mid+1;i++){
              for(j=1;j<=m-mid+1;j++){
                  if(check(i,j,mid)){
                     ans=mid; 
                     break;
                  }
              }
              if(o)break;
           }
           if(o){
              l=mid+1;
              o=0;
           }else r=mid-1;
    }
    printf("%d",ans);
    return 0;
}
用二分答案,暴力解法(二分只是你的谎言doge),在矩形里截取正方形,边长最大为矩形长宽的最小值,最小是0,从这两个区间里找答案即可。

遍历点时正方形的彼岸可能会越出矩形,所以i从1循环到n-mid+1,j从1循环到m-mid+1,这样既可以避免不必要的错误,又节省了空间和时间。

该方法的时间复杂度跟spfa一样,非常玄学,看似至少有O(n4)的时间复杂度,但是实测的时候实际复杂度可能不到O(n4)。

三.三角蛋糕

链接在这(doge)

该题和盖房子基本可以说是完全一致,但是需要注意三角形的朝向问题(用最左边小三角的横坐标的奇偶即可判断),也是两种方法。

posted @ 2024-02-17 19:45  无敌の暗黑魔王  阅读(26)  评论(1)    收藏  举报