Loading

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;
}
posted @ 2022-08-05 21:01  ZCETHAN  阅读(22)  评论(0编辑  收藏  举报