多维动态规划

ACM题集:https://blog.csdn.net/weixin_39778570/article/details/83187443
动态规划:https://blog.csdn.net/weixin_39778570/article/details/87014343

P1508 Likecloud-吃、吃、吃

给定一个二维表,从最下一行中间遍历到最顶行,输出途径的格子和的最大值
--------------------------------------------------------------
正序dfs,逆序dp
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;

int n,m,a[205][205],dp[205][205];
int dx[] = {-1,-1,-1};
int dy[] = {-1,0,1};
int dfs(int x, int y){
	if(x==1)return dp[x][y] = a[x][y];
	if(dp[x][y])return dp[x][y];
	int t=-99999;
	fo(i,0,2){
		int nowx = x+dx[i];
		int nowy = y+dy[i];
		if(nowx>=1&&nowx<=n&&nowy>=1&&nowy<=m){
			t = max(t,dfs(nowx,nowy));
		}
	}
	return dp[x][y] = t+a[x][y];
}
void DP(){
	fo(i,1,n)fo(j,1,m){
		dp[i][j] = a[i][j] + max(dp[i-1][j],max(dp[i-1][j-1],dp[i-1][j+1]));
	}
	cout<<max(dp[n][m/2],max(dp[n][m/2+1],dp[n][m/2+2]));
}
int main(){
	cin>>n>>m;
//	memset(a,-9999,sizeof(a));
	dp[n+1][m/2+1]=0;
	fo(i,1,n)fo(j,1,m)scanf("%d",&a[i][j]);
	cout<<dfs(n+1,m/2+1); 
//	DP();
	return 0;
}

P1006 传纸条

从左上传到右下,和从右下传到左上,同一个格子不能重复使用,求途径格子和最大值
---------------------------------------------------------------------
可以看做两个左上传递到右下,枚举两个格子进行dp,还有个特性,就是两个格子的横轴坐标和相等
#include<bits/stdc++.h>
#define fo(i,j,n) for(int i=j; i<=n; ++i)
#define ll long long
using namespace std;

int n,m;
int f[205][105][105],a[105][105];
int my_max(int a, int b, int c, int d){
    return max(max(a,b),max(c,d));
}
void solve1(){
	fo(k,2,m+n){ // 横纵坐标和相等 
        fo(i,1,n)fo(j,1,n){ // 枚举两纵坐标
            if(k-i<1 || k-j<1)continue;
            f[k][i][j] = my_max(f[k-1][i-1][j],f[k-1][i-1][j-1],f[k-1][i][j-1], f[k-1][i][j]) + a[i][k-i]+a[j][k-j];
            if(i==j) f[k][i][j] -= a[i][k-i]; // 同一个格子上只能踩一次 
        }
    }
    cout<<f[m+n][n][n]<<endl;
}
int F[105][105][105][105];
void solve2(){
	fo(aa,1,n)fo(b,1,m){
		fo(c,1,n)fo(d,1,m){
			F[aa][b][c][d] = my_max(F[aa-1][b][c-1][d],F[aa-1][b][c][d-1],F[aa][b-1][c-1][d],F[aa][b-1][c][d-1])+a[aa][b]+a[c][d];
			if(aa==c&&b==d)F[aa][b][c][d]-=a[aa][b]; // 同一个格子上只能踩一次 
		}
	}
	cout<<F[n][m][n][m]<<endl;
} 
int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n)fo(j,1,m)scanf("%d",&a[i][j]);
	//solve1();
	solve2();
    return 0;
}

P1387 最大正方形

找出n*m网格中最大的正方形。1代表格子有,0代表格子无
------------------------------------------------------
可以枚举左上角和长度,然后通过前缀和判断是否是正方形,暴力做法
可以通过枚举右下角的1进行dp,f[i][j]表示以a[i][j]为最下角的正方形的最大长度
f[i][j] = min(min(f[i-1][j],f[i][j-1]), f[i-1][j-1]) + 1;
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(int i=j; i<=n; ++i)
using namespace std;
const int maxn = 105;
int n,m,a[maxn][maxn];
int f[105][105],ans;
void solve1(){
	fo(i,1,n)fo(j,1,m){ // 枚举右下角 
		if(a[i][j]==1){
			f[i][j] = min(min(f[i-1][j],f[i][j-1]), f[i-1][j-1]) + 1;
			ans = max(ans,f[i][j]);
		}
	}
	cout<<ans<<endl; 
}
// 暴力 
int mp[maxn][maxn];
void solve2(){
	fo(i,1,n)fo(j,1,m){
		mp[i][j] = mp[i-1][j] + mp[i][j-1] - mp[i-1][j-1] + a[i][j];
	}
	int size = min(n,m);
	fo(i,1,n)fo(j,1,m)fo(len,1,size){ // 枚举左上角,好正方形长度 
		if(i+len-1<=n && j+len-1<=m){
			if(mp[i+len-1][j+len-1] - mp[i-1][j+len-1] - mp[i+len-1][j-1] + mp[i-1][j-1] == len*len) 
				ans = max(ans,len);
		}
	} 
	cout<<ans<<endl;
}
int main(){
	scanf("%d%d",&n,&m);
	fo(i,1,n)fo(j,1,m)scanf("%d",&a[i][j]);
//	solve1();
	solve2();
	return 0;
}

P1417 烹调方案

一共有n件食材,每件食材有三个属性,ai,bi和ci,
如果在t时刻完成第i样食材则得到ai-t*bi的美味指数,
用第i件食材做饭要花去ci的时间。现在有总时间T
-------------------------------------------------------------------------------
这道题不仅要确定是否选择该食材,而且选择食材的顺序也会影响到最终的结果
所以我们可以先尝试能不能先把顺序确定下来,依照这个顺序去决定选择或者不选择 

如果没有b[i]这个属性的话就是明显的01背包问题。
现在考虑相邻的两个物品x,y。假设现在已经耗费p的时间,那么分别列出先做x,y的代价:

a[x]-(p+c[x])*b[x]+a[y]-(p+c[x]+c[y])*by	
a[y]-(p+c[y])*b[y]+a[x]-(p+c[y]+c[x])*bx

对这两个式子化简,得到①>②的条件是c[x]*b[y]<c[y]*b[x].	
发现只要满足这个条件的物品对(x,y),x在y前的代价永远更优。	
因此可以根据这个条件进行排序,之后就是简单的01背包了
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(ll i=j; i<=n; ++i) 
using namespace std;

struct node{
	ll a,b,c;
	bool operator < (const node & p)const{
		return c*p.b<p.c*b;
	} 
}A[100];
ll T,n,f[100005],ans;
void solve(){
	sort(A+1,A+n+1);
	fo(i,1,n){
		for(int j=T; j-A[i].c>=0; j--){
			f[j] = max(f[j], f[j-A[i].c]+A[i].a-A[i].b*j);
		}
	}
	fo(i,1,T)ans = max(ans,f[i]);
	cout<<ans<<endl;
}
int main(){
	scanf("%lld%lld",&T,&n);
	fo(i,1,n)scanf("%lld",&A[i].a);
	fo(i,1,n)scanf("%lld",&A[i].b);
	fo(i,1,n)scanf("%lld",&A[i].c);
	solve();
	return 0;
} 

P1855 榨取kkksc03

有T时间,M金钱,n个任务, 每个任务需要t[i]时间,a[i]金钱,最多可以完成多少个任务
---------------------------------------------------------------------------
二维01背包
#include<bits/stdc++.h>
#define ll long long 
#define fo(i,j,n) for(ll i=j; i<=n; ++i)
using namespace std;
struct node{
	ll a,b;
	bool operator < (const node &p)const{
		return a<p.a; 
	}
}A[105]; 
ll n,M,T,f[205][205],ans; 
void solve(){
//	sort(A+1,A+1+n);
	fo(i,1,n){
		for(ll j=M; j-A[i].b>=0; j--){ // 钱 
			for(ll k=T; k-A[i].a>=0; k--){ // 时间 
				f[j][k] = max(f[j][k], f[j-A[i].b][k-A[i].a]+1);
				ans = max(f[j][k],ans);
			}
		}
	}
	cout<<ans<<endl;
}
int main(){
	scanf("%lld%lld%lld",&n,&M,&T);
	fo(i,1,n)scanf("%lld%lld",&A[i].a,&A[i].b);
	solve();
	return 0;
}

P1736 创意吃鱼法

找到最长对角线的正方形,正方形内除了对角线为1,其他格子为0
-----------------------------------------------------
类似最大正方形做法
我们在枚举右下角为1的格子时,需要知道同行最长有多少个连续0,同列最长有多少个连续0
然后与前一个状态取min进行dp。f[i][j]表示以i,j,为右下角的最长对角线正方形的长度。。。
  f[i][j] = min(f[i-1][j-1], min(s1[i][j-1],s2[i-1][j]))+1;
  其中s1表示纵向连续0的长度,s2表示横向
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(ll i=j; i<=n; ++i)
using namespace std;
const int maxn = 2505;
int n,m,A[maxn][maxn],f[maxn][maxn],ans;
int s1[maxn][maxn],s2[maxn][maxn]; // 记录0的个数 
// 类似最大正方形做法 
void solve2(){
	// 左 
    for(int i=1; i<=n; i++)
    for(int j=1; j<=m; j++){
        if(!A[i][j]){
            s1[i][j] = s1[i][j-1]+1; // 纵 
            s2[i][j] = s2[i-1][j]+1; // 横 
        }	
        if(A[i][j]){
            f[i][j] = min(f[i-1][j-1], min(s1[i][j-1],s2[i-1][j]))+1;
        }
        ans = max(ans,f[i][j]);
    }
    // 右 
    memset(f,0,sizeof(f));
    memset(s1,0,sizeof(s1));
    memset(s2,0,sizeof(s2));
    for(int i=1; i<=n; i++)
    for(int j=m; j>=1; j--){
        if(!A[i][j]){
            s1[i][j] = s1[i][j+1]+1;
            s2[i][j] = s2[i-1][j]+1;
        }	
        if(A[i][j]){
            f[i][j] = min(f[i-1][j+1], min(s1[i][j+1],s2[i-1][j]))+1;
        }
        ans = max(ans,f[i][j]);
    }
    cout<<ans<<endl;
} 
int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n)fo(j,1,m){
        scanf("%d",&A[i][j]);
    } 
    solve2();
    return 0;
}

解法2:暴力法

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(ll i=j; i<=n; ++i)
using namespace std;
const int maxn = 3000;
int n,m,A[maxn][maxn],sum[maxn][maxn],f[maxn][maxn],ans;
int query(int x1, int y1, int x2, int y2){
	return sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1];
}
// 四个方向进行递推,其实两个方向就可以,但是两个方向出现WA???...,然后有两组数据被卡掉(把long类型该int就过了)
void solve(){
    for(int x=1;x<=n;x++)for(int y=1;y<=m;y++){
        if(A[x][y]){
            f[x][y]=1;
            if(f[x-1][y-1]&&x-1>0&&y-1>0){
                int t = f[x-1][y-1];
                if(query(x,y-t,x,y-1)==0&&query(x-t,y,x-1,y)==0)f[x][y]=t+1; // 不严格正确的解,可能f 在 1和t+1之间 
            }
            ans = max(ans,f[x][y]);
        }
    }
    memset(f,0,sizeof(f));
    for(int x=n; x>=1; x--)for(int y=1; y<=m; y++){
        if(A[x][y]){
            f[x][y]=1;
            if(f[x+1][y-1]&&y-1>0&&x+1<=n){
                int t = f[x+1][y-1];
                if(query(x,y-t,x,y-1)==0&&query(x+1,y,x+t,y)==0)f[x][y]=t+1;
            }
            ans = max(ans,f[x][y]);
        }
    }
    memset(f,0,sizeof(f));
    for(int x=n; x>=1; x--)for(int y=m; y>=1; y--){
        if(A[x][y]){
            f[x][y]=1;
            if(f[x+1][y+1]&&x+1<=n&&y+1<=m){
                int t = f[x+1][y+1];
                if(query(x,y+1,x,y+t)==0&&query(x+1,y,x+t,y)==0)f[x][y]=t+1;
            }
            ans = max(ans,f[x][y]);
        }
    }
    memset(f,0,sizeof(f));
    for(int x=1; x<=n; x++)for(int y=m; y>=1; y--){
        if(A[x][y]){
            f[x][y]=1;
            if(f[x-1][y+1]&&x-1>0&&y+1<=m){
                int t = f[x-1][y+1];
                if(query(x,y+1,x,y+t)==0&&query(x-t,y,x-1,y)==0)f[x][y]=t+1;
            }
            ans = max(ans,f[x][y]);
        }
    }
    cout<<ans<<endl;
}

int main(){
	scanf("%d%d",&n,&m);
	fo(i,1,n)fo(j,1,m){
		scanf("%d",&A[i][j]);
		sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+A[i][j];
	} 
	solve();
	return 0;
} 
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页