一类适合记忆化搜索的区间dp

https://www.luogu.com.cn/problem/P5752

https://codeforces.com/contest/598/problem/E

cf这个题考虑dp预处理,状态是三维的,转移是分割方案和所分块需要获得的巧克力数量。最后题目多次询问可以o(1)快速查询的

// Problem: E. Chocolate Bar
// Contest: Codeforces - Educational Codeforces Round 1
// URL: https://codeforces.com/contest/598/problem/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
# define int long long
#define ull unsigned long long
#define pii pair<int,int>

#define baoliu(x, y) cout << fixed << setprecision(y) << x
#define endl  "\n"
#define debug1(x) cerr<<x<<" "
#define  debug2(x) cerr<<x<<endl
const int N = 35;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-8;
int n, m,k;
int dp[N][N][N*N];

int a[N];
//考虑区间dp,记忆化搜索
//提前预处理答案,O(1)查询
//30*30*900(状态)*(60*30)转移=48600000*30(1.2e8)
//不会跑满,加上记忆化和行列地位对等的剪枝
int dfs(int x,int y,int cnt){
	if(cnt==0||x*y==cnt)return 0;
	int &res=dp[x][y][cnt];
	//res=1e18;
	if(res!=-1)return res;
	
	if(dp[y][x][cnt]!=-1)return res=dp[y][x][cnt];
	res=1e18;
	for(int i=1;i<=x-1;i++){
		int now=cnt;
		for(int c=0;c<=now;c++){
			res=min(res,dfs(i,y,c)+dfs(x-i,y,now-c)+y*y);
		}
	}
	for(int i=1;i<=y-1;i++){
		int now=cnt;
		for(int c=0;c<=now;c++){
			res=min(res,dfs(x,i,c)+dfs(x,y-i,now-c)+x*x);
		}
	}
	dp[y][x][cnt]=res;
	return res;
}
void solve(){
	cin>>n>>m>>k;
	cout<<dfs(n,m,k)<<endl;
}
signed main() {
    cin.tie(0);
    
    ios::sync_with_stdio(false);
memset(dp,-1,sizeof dp);
    int t;
   cin>>t;
    // t=1;
    while (t--) {
solve();
    }
    return 0;
}

NOI1999

典型的记忆化搜索区间dp,根据方差公式变形,判断每次转移是根据切割方式和选择继续在哪部分上切割,不要的那部分的方差贡献需要预处理二维前缀和快速查询

// Problem: 棋盘分割
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/323/
// Memory Limit: 10 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
//# define int long long
#define ull unsigned long long
#define pii pair<int,int>

#define baoliu(x, y) cout << fixed << setprecision(y) << x
#define endl  "\n"
#define debug1(x) cerr<<x<<" "
#define  debug2(x) cerr<<x<<endl
const int N = 17;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const double eps = 1e-8;
int n, m;
int a[N][N];

double avg=0;
double get(int x1, int y1,int x2,int y2){
	double res=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
	res-=avg;
	return res*res;
}
//只需要割k-1次
double f[N][N][N][N][N];
double dp(int k,int x1,int y1,int x2,int y2){
	double &u=f[k][x1][y1][x2][y2];
	if(u>=0)return u;
	if(k==m){
		//不存在第m次切割,m-1次以后就满足要求,直接返回这个棋盘的贡献
		return u=get(x1,y1,x2,y2);
	}
	double tmp=1e9;
	for(int i=x1;i<x2;i++){
		tmp=min(tmp,dp(k+1,x1,y1,i,y2)+get(i+1,y1,x2,y2));
	  tmp=min(tmp,dp(k+1,i+1,y1,x2,y2)+get(x1,y1,i,y2));
		
		// tmp=min(tmp,dp[k+1][x1][y1][i][y2]+get(i+1,y1,x2,y2));
		// tmp=min(tmp,dp[k+1][i+1][y1][x2][y2]+get(x1,y1,i,y2));
	}
	for(int i=y1;i<y2;i++){
	tmp=min(tmp,dp(k+1,x1,y1,x2,i)+get(x1,i+1,x2,y2));
	tmp=min(tmp,dp(k+1,x1,i+1,x2,y2)+get(x1,y1,x2,i));
	
	// tmp=min(tmp,dp[k+1][x1][y1][x2][i]+get(x1,i+1,x2,y2));
	// tmp=min(tmp,dp[k+1][x1][i+1][x2][y2]+get(x1,y1,x2,i));
	}
	return u=tmp;
}
void solve(){
	n=8;
cin>>m;
	for(int i=1;i<=n;i++){
       for(int j=1;j<=8;j++){
       	cin>>a[i][j];
       }
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]+=a[i-1][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]+=a[i][j-1];
		}
	}
	avg=(double)a[n][n]/(double)m;
//	cerr<<avg<<endl;
	memset(f,-1,sizeof f);
	//初始状态有一个盘
//当前已经对棋盘k-1次切割,有k个棋盘
//k次划分后选择的棋盘是 左上角为 x1y1,右下角x2y2
	double ans=dp(1,1,1,n,n);
//	cerr<<ans<<endl;
	ans=sqrt(ans/(double)m);
	baoliu(ans,3);
}
int main() {
    cin.tie(0);
    
    ios::sync_with_stdio(false);

    int t;
   //cin>>t;
     t=1;
    while (t--) {
solve();
    }
    return 0;
}

posted @ 2024-03-27 23:09  potential-star  阅读(2)  评论(0编辑  收藏  举报