P2140小Z的电力管制-题解

似乎这道题和 P1436 以及 P5752 的棋盘分割有很多相似之处 。 (三倍经验)
题目对于电力的要求似乎有些复杂 。
简化来说就是 取各方案各个区域分值之和最小值最大
特来此写一篇较为详细的题解 。

1.思路

类似于分割棋盘,我们可以使用进行 递归 分割 。
二维取区块和可以使用 二维前缀和
但是答案有两个,可以使用结构体存储并维护 。
\(f\) 数组进行 记忆化处理 优化时间复杂度 。

2.解法

易知递归边界是不可再分,即已经缩小到一个点 。
电力消耗值为所有需要的电 \(tot\) 减去当前断电区域断掉的电 \(broke\)
所有电力剩余值为 供电总值 - 电力消耗值
定义:

  • \(ans\) 为方案总数 。
  • \(least\) 为电力剩余值 。

如果当前方案较多,则替换已有方案,更新电力剩余值 。
如果方案数相同,则比较电力剩余值,选出较大的 。

3.Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=35;
struct Node{
	int ans;
	int least;
}f[maxn][maxn][maxn][maxn];
int a[maxn][maxn];
int sum[maxn][maxn];
int tot=0;
int n,m,u;
int get_sum(int x1,int y1,int x2,int y2){
	int k=sum[x2][y2]+sum[x1-1][y1-1]-sum[x1-1][y2]-sum[x2][y1-1];//求区块分值和 
	return k;
}
Node dfs(int x1,int y1,int x2,int y2,int broke){
	if(f[x1][y1][x2][y2].ans) return f[x1][y1][x2][y2];
	Node tmp;tmp.ans=1;tmp.least=u-tot+broke;
	if(x1==x2&&y1==y2) return tmp;//递归边界 
	for(int i=x1;i<x2;i++){//纵向分割 
		int area1=get_sum(x1,y1,i,y2),area2=get_sum(i+1,y1,x2,y2);
		if(area1<=u&&tot-area1<=u&&area2<=u&&tot-area2<=u){
			Node ans1=dfs(x1,y1,i,y2,area1),ans2=dfs(i+1,y1,x2,y2,area2);
			if(ans1.ans+ans2.ans>tmp.ans){//替换已有方案,更新电力剩余值 
				tmp.ans=ans1.ans+ans2.ans;
				tmp.least=min(ans1.least,ans2.least);
			}
			//比较电力剩余值,选出较大的
			else if(ans1.ans+ans2.ans==tmp.ans) tmp.least=max(tmp.least,min(ans1.least,ans2.least));
		}
	}
	for(int i=y1;i<y2;i++){//横向分割 
		int area1=get_sum(x1,y1,x2,i),area2=get_sum(x1,i+1,x2,y2);
		if(area1<=u&&tot-area1<=u&&area2<=u&&tot-area2<=u){
			Node ans1=dfs(x1,y1,x2,i,area1),ans2=dfs(x1,i+1,x2,y2,area2);
			if(ans1.ans+ans2.ans>tmp.ans){
				tmp.ans=ans1.ans+ans2.ans;
				tmp.least=min(ans1.least,ans2.least);
			}
			else if(ans1.ans+ans2.ans==tmp.ans) tmp.least=max(tmp.least,min(ans1.least,ans2.least));
		}
	}
	return f[x1][y1][x2][y2]=tmp;
}
int main(){
	scanf("%d%d%d",&n,&m,&u);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) scanf("%d",&a[i][j]),tot+=a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];//二维前缀和 
	Node res=dfs(1,1,n,m,tot);
	printf("%d %d",res.ans,res.least);
	return 0;
}
posted @ 2022-06-06 16:58  ThinkGone  阅读(56)  评论(0编辑  收藏  举报