Problem - 1580A - Codeforces
CQXYM发现了一个大小为n×m的矩形。矩形由n行m列的方块组成,每个方块可以是黑曜石方块或空方块。CQXYM可以通过一次操作将黑曜石方块变为空方块,或将空方块变为黑曜石方块。
一个大小为a×b的矩形M被称为传送门,当且仅当满足以下条件:
a≥5,b≥4
对于所有1<x<a,方块Mx,1和Mx,b都是黑曜石方块。
对于所有1<x<b,方块M1,x和Ma,x都是黑曜石方块。
对于所有1<x<a,1<y<b,方块Mx,y都是空方块。
M1,1、M1,b、Ma,1、Ma,b可以是任意类型的方块。
注意,行数必须是a,列数必须是b,而不是相反的。
注意角落的方块可以是任意类型的。
CQXYM想知道他至少需要进行多少次操作才能使至少一个子矩形成为传送门。
输入: 第一行包含一个整数t(t≥1),表示测试用例的数量。
对于每个测试用例,第一行包含两个整数n和m(5≤n≤400,4≤m≤400)。
然后是n行,每行包含m个字符0或1。如果第i行的第j个字符为0,则方块Ai,j为一个空方块。否则,方块Ai,j为一个黑曜石方块。
保证所有测试用例中n的总和不超过400。
保证所有测试用例中m的总和不超过400。
输出: 输出t个答案,每个答案占一行。
Examples
Input
Copy
1 5 4 1000 0000 0110 0000 0001
Output
Copy
12
Input
Copy
1 9 9 001010001 101110100 000010011 100000001 101010101 110001111 000001111 111100000 000110000
Output
Copy
5
题解:
可以想到的思路是,我们枚举矩形的上下左右,但是这样的时间复杂的就是O(n^4)
所以想想如何优化掉一维
假设我们枚举上下界,我们可以利用前缀和,以方便,快速求出一列多少需要改变
接着枚举列j
假设sum为到当前位置,需要改变的值(除了,边界均为0)
cnt为到当前位置,依次为边界需要,这一列为边界需要改变的值
假设i为左边界,j为有边界
sum[j - 1] + cnt[j] - sum[i] + cnt[i],即为所求
我们发现只要维护好,cnt[i] - sum[i]的最小值,优化掉一维,不必再枚举左边界
#include <cstdio>
#include <cstring>
#include <algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
#define int long long
typedef pair<int,int> PII;
typedef unsigned long long ULL;
const int N = 5e5*2 + 10;
int mod = 998244353;
int a[405][505];
int s[405][405];
int pre[N];
void solve()
{
int n,m;
cin >> n >> m;
for(int i = 1;i <= n;i++)
{
string t;
cin >> t;
t = " " + t;
for(int j = 1;j <= m;j++)
{
a[i][j] = t[j] - '0';
s[i][j] = s[i - 1][j] + a[i][j];
}
}
int ans = 1e9;
pre[0] = 1e9;
for(int i = 1;i <= n;i++)
{
for(int j = i + 4;j <= n;j++)
{
int sum = 0;
for(int k = 1;k <= m;k++)
{
int x = s[j - 1][k] - s[i][k];
int y = 2 - a[i][k] - a[j][k];
int w = j - i - 1 - x;
if(k > 3)
{
ans = min(ans,pre[k - 3] + sum + w);
}
sum += x + y;
pre[k] = min(pre[k - 1],w - sum);
}
}
}
cout << ans <<"\n";
}
signed main()
{
ios::sync_with_stdio(0 );
cin.tie(0);cout.tie(0);
int t = 1;
cin >> t;
while(t--)
{
solve();
}
}