现在才发现比赛时读错题意了。。。以为要连通的1的个数不能超过
呢。
题意:
给你一个矩形,然后0位黑格,1位白格,现在你可以横切或者纵切这个矩形,使得每块的白块的个数<=k。
思路:
遇见过不少这种题了。
由于某一个坐标轴的范围很小,而另一个很大,所以可以二进制枚举范围小的坐标轴的情况。然后遍历列判断就行了。
由于写的时候没带脑子,各种bug,调了很久。
然后,其实可以直接求预处理二维前缀和,这样,常数就会小点吧,谁让我没带脑子呢,就这样吧
复杂度
int w[20][2000];
int x[2000][20];
bool vis[1005];
//总体思路,先把行切开,再去切列
int main(){
int n = read(),m = read(),k = read();
rep(i,1,n){
rep(j,1,m){
char c;
cin >> c;
w[i][j] = c -'0';
x[j][i] = x[j][i-1] + w[i][j];//预处理每列的前缀和
}
}
int S = (1 << (n));
int ans = INF;
for(int q = 1;q <= S-1;++ q){//二进制枚举每种切割情况,第i位为1,则从第(i+1)行下面切一刀
int p = q;
p |= (1<<(n-1));//我们为了处理方便,在最后一行后面留一刀,最后减掉即可
vector<int> Q[20];
int tot = 0;
int inx = 1;//控制每个连通块的行
bool r = 1;
for(int i = 0;i < n;++i){
if((p>>i)&1){
tot ++;
rep(j,1,m){
int d = x[j][i+1] - x[j][inx-1];
if(d > k) {r = 0;break;}
Q[tot].push_back(d);
}
inx = i + 2;
}
if(r == 0) break;
}
if(r == 0) continue;
int h = 0;
/*
这儿判断列需要切几刀,我一开始想着贪心去切,取切列的最大刀数,可是一直有5组Wa,
后来相出一种情况,发现不对劲,然后只能m*n判断了,额....刚才似乎是n*m,复杂度一样嘛......
*/
int Sum[12] = {0};
for(int i = 0;i <= m-1;++i){
bool op = 1;
for(int j = 1;j <= tot;++j){
Sum[j] += Q[j][i];
if(Sum[j]>k){op = 0;break;}
}
if(!op){
h ++;
rep(j,1,tot) Sum[j] = Q[j][i];
}
}
ans = min(ans,tot-1+h);
}
if(ans == INF) ans = 0;
cout << ans;
}