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;
}