题目链接:https://atcoder.jp/contests/abc159/tasks/abc159_e
题目大意
有一块由$H*S$个小正方形组成的$H$行$W$列的长方形区域,其中小正方形有黑白两种,现需要将长方形区域拆分,只能水平切或者垂直切(不能拐弯,只能一切到底),问最少切几次,能使得切出来的每一小块中白色方块的数量少于或等于$K$个?
分析
由题目所给数据可以发现,行数很少,因此可以暴力枚举所有水平切法,然后竖直方向可以利用贪心来解决.
代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 /*-------------------Define Start-------------------*/ 5 typedef bool BL; // 布尔类型 6 typedef char SB; // 有符号1字节,8位 7 typedef unsigned char UB; // 无符号1字节,8位 8 typedef short SW; // 有符号短整型,16位 9 typedef unsigned short UW; // 无符号短整型,16位 10 typedef long SDW; // 有符号整型,32位 11 typedef unsigned long UDW; // 无符号整型,32位 12 typedef long long SLL; // 有符号长整型,64位 13 typedef unsigned long long ULL; // 无符号长整型,64位 14 typedef char CH; // 单个字符 15 typedef float R32; // 单精度浮点数 16 typedef double R64; // 双精度浮点数 17 18 #define Rep(i, n) for (register SDW i = 0; i < (n); ++i) 19 #define For(i, s, t) for (register SDW i = (s); i <= (t); ++i) 20 #define rFor(i, t, s) for (register SDW i = (t); i >= (s); --i) 21 #define foreach(i, c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 22 #define ms0(a) memset(a,0,sizeof(a)) 23 #define msI(a) memset(a,0x7f,sizeof(a)) 24 #define LOWBIT(x) ((x)&(-x)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 31 #define pr(x) cout << #x << " = " << x << " " 32 #define prln(x) cout << #x << " = " << x << endl 33 34 const ULL mod = 1e9 + 7; //常用模数(可根据题目需要修改) 35 const ULL inf = 0x7fffffff; //用来表示无限大 36 const ULL infLL = 0x7fffffffffffffffLL; //用来表示无限大 37 /*-------------------Define End-------------------*/ 38 39 const UDW maxN = 1e3 + 7; 40 SDW H, W, K; 41 SDW S[13][maxN]; 42 SDW ans = inf; 43 44 SDW getSum(SDW x1, SDW y1, SDW x2, SDW y2) { 45 return S[x2][y2] - S[x2][y1 - 1] - S[x1 - 1][y2] + S[x1 - 1][y1 - 1]; 46 } 47 48 void input(){ 49 cin >> H >> W >> K; 50 For(i, 1, H) { 51 For(j, 1, W) { 52 scanf("%1d", &S[i][j]); 53 S[i][j] += S[i - 1][j] + S[i][j - 1] - S[i - 1][j - 1]; // 记录为前缀和,方便计算 54 } 55 } 56 } 57 58 void solve(){ 59 // 枚举水平方向上的所有切法 60 Rep(i, 1 << (H - 1)) { 61 // 在竖直方向上采用贪心切法 62 vector< SDW > pos; // 记录水平切的位置 63 SDW tmp = i; 64 SDW ret = __builtin_popcount(i); 65 66 pos.PB(0); 67 while(tmp) { 68 SDW x = LOWBIT(tmp); 69 tmp &= tmp - 1; 70 71 pos.PB(__builtin_ctz(x) + 1); 72 } 73 pos.PB(H); 74 75 SDW a = 1, b = 1; 76 77 while(b <= W) { 78 For(j, 1, pos.size() - 1) { 79 if(getSum(pos[j - 1] + 1, a, pos[j], b) > K) { 80 if(a == b) { // 无法切开的情况,直接跳出 81 b = W + 1; 82 ret = inf; 83 break; 84 } 85 else { 86 ++ret; 87 a = b; 88 --b; 89 break; 90 } 91 } 92 } 93 ++b; 94 } 95 96 ans = min(ans, ret); 97 } 98 } 99 100 void output(){ 101 cout << ans << endl; 102 } 103 104 int main() { 105 input(); 106 solve(); 107 output(); 108 return 0; 109 }