AtCoder E - Dividing Chocolate

现在才发现比赛时读错题意了。。。以为要连通的1的个数不能超过 k k 呢。
题意:
给你一个矩形,然后0位黑格,1位白格,现在你可以横切或者纵切这个矩形,使得每块的白块的个数<=k。
思路:
遇见过不少这种题了。
由于某一个坐标轴的范围很小,而另一个很大,所以可以二进制枚举范围小的坐标轴的情况。然后遍历列判断就行了。
由于写的时候没带脑子,各种bug,调了很久。
然后,其实可以直接求预处理二维前缀和,这样,常数就会小点吧,谁让我没带脑子呢,就这样吧
复杂度 O ( 2 n n m ) O(2^n\cdot n\cdot m)

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

发布了636 篇原创文章 · 获赞 38 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_43408238/article/details/105120264
今日推荐