AT3609 Poor Penguin
模拟赛题!惨遭 \(\texttt{m}\color{red}{\texttt{aoweishou}}\) 爆切,以及假算的乱草。。。
题意
有一个网格,起初里面只有 1
或者 2
。每一时刻,对于 2
不会改变,对于 1
如果上方和下方中有一个是 0
,并且左边和右边有一个是 0
,那么这个 1
会变成 0
。你可以把一些 2
改成 1
,每改一次代价是 \(1\)。
现在给出一个位置 \((x,y)\),求使得这个位置变成 0
的最小代价。
网格之外是 0
。
Solution
赛时想到的假算:
考虑这个变成 0
肯定是从 \(4\) 个角开始,然后向中间靠拢,每次可以把形如:
x0
01
的 1
改成 0
。考虑每一个 2
会使得从某个方向过来的一片矩形不可达。那么对于你要变成 0
的那个点,分别计算从左上左下右上右下到达需要破坏的 2
的个数,取个 \(\min\) 就是答案。
Hack:
你考虑有可能会出现以下情况:
...20...
...01...
...02...
这样一来的话,那个 1
可以看成是从左下和右上共同变成 0
的,无法被上述算法统计。
Real Solution:
你就考虑怎么解决这个 Hack。这个 Hack 的关键所在就是你可以让从两个角开始的路径相遇,然后把剩下的两块变得相对独立起来。对于这块独立的块,我们可以继续利用上面的假算。(独立的意思很好理解,就是如果这个矩形被 0
包围,就可以完全独立地计算)
当然,不一定就只是独立一层。所以我们要枚举所有包含 \((x,y)\) 的矩形,然后计算将这个矩形变得独立的最小代价,再加上上面的假算应用于这个矩形得到的答案,就是你枚举当前矩形独立的答案。最终在所有情况下取最小值就行了。
为了求每一块独立的最小代价,我们可以采用 dp,即 \(dp[a][b][c][d]\) 表示矩形 \((a,b)-(c,d)\) 独立的最小代价,可以从外向内递推了。
Code
// Problem: AT3609 Poor Penguin
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT3609
// Memory Limit: 3988 MB
// Time Limit: 250000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
// #define int long long
using namespace std;
int dp[45][45][45][45],pre[45][45];
int n,m,x,y;char mp[45][45];
int calc(int a,int b,int c,int d){
return pre[c][d]-pre[c][b-1]-pre[a-1][d]+pre[a-1][b-1];
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
rep(i,1,n) rep(j,1,m){
cin>>mp[i][j];
if(mp[i][j]=='P') x=i,y=j;
if(mp[i][j]=='#') pre[i][j]=1;
}rep(i,1,n) rep(j,1,m) pre[i][j]+=pre[i][j-1];
rep(i,1,n) rep(j,1,m) pre[i][j]+=pre[i-1][j];
memset(dp,0x3f,sizeof(dp));
dp[1][1][n][m]=0;
int ans=pre[n][m];
rep(a,1,n) rep(b,1,m) per(c,n,a) per(d,m,b) if(a<=x&&b<=y&&c>=x&&d>=y){
rep(i,a-1,c) rep(j,b-1,d){
if((i==a-1||i==c)&&(j==b-1&&j==d)) continue;
dp[a][b][i][j]=min(dp[a][b][i][j],dp[a][b][c][d]+calc(a,j+1,i,d)+calc(i+1,b,c,j));
dp[a][j+1][i][d]=min(dp[a][j+1][i][d],dp[a][b][c][d]+calc(a,b,i,j)+calc(i+1,j+1,c,d));
dp[i+1][b][c][j]=min(dp[i+1][b][c][j],dp[a][b][c][d]+calc(a,b,i,j)+calc(i+1,j+1,c,d));
dp[i+1][j+1][c][d]=min(dp[i+1][j+1][c][d],dp[a][b][c][d]+calc(a,j+1,i,d)+calc(i+1,b,c,j));
}
ans=min(ans,dp[a][b][c][d]+min(min(calc(a,b,x,y),calc(a,y,x,d)),min(calc(x,b,c,y),calc(x,y,c,d))));
}
cout<<ans<<'\n';
return 0;
}