ZOJ Monthly, June 2012 - F Choir III - zoj 3616
题目说找一个 子矩阵,里面价值最大,其中男生人数 >= b, 女生人数 >= g, 中间不能有不会唱歌的人。
这题如果关键在于 排除不会唱歌的人,思想肯定是贪心思想,想人数越多越好,男生女生不过就是在if里面多判定的一句话罢了。
为了方便理解,我画张很丑的图...
蓝色表示我当前扫到的队员,当然他是会唱歌的。
红色表示不会唱歌的队员。
白色表示,没选择的队员。
我在扫描时,对于每一个点是一行行扫,这样的话,复杂度是 N*M*N。
先扫第当前行,也就是第4行,因为第四行上面之前的队员我都会唱歌,那我尽量全部选走。
粉色代表这次选择的队员。
再上一行,因为有不会唱歌的队员,那我只能放弃第一列之前的所有成员。
同理在上一行是:
最后因为不会唱歌的队员和蓝色点成员在一列了,也就是说在这列之前是不会选成员了,即:
扫描结束,另外其中对于子矩阵的能力值的和可以在一开始预处理掉,后面的询问就是O(1)了。
我在代码中用list[i][j] 表示当前i行,第j个队员之前离j最近的不会唱歌的队员编号。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<map> 7 #include<vector> 8 using namespace std; 9 #define N 110 10 #define M 2010 11 #define inf 2000000000 12 typedef long long LL; 13 int sum[N][M], sboy[N][M], list[N][M]; 14 int sv, sb, sg; 15 void solve(int x1, int y1, int x2, int y2) { 16 if (x1 > x2 || y1 > y2) return ; 17 sv = sum[x2][y2] - sum[x1-1][y2] - sum[x2][y1-1] + sum[x1-1][y1-1]; 18 sb = sboy[x2][y2] - sboy[x1-1][y2] - sboy[x2][y1-1] + sboy[x1-1][y1-1]; 19 sg = (x2-x1+1)*(y2-y1+1) - sb; 20 } 21 int main() { 22 int n, m, b, g; 23 memset(sum, 0, sizeof(sum)); 24 memset(sboy, 0, sizeof(sboy)); 25 memset(list, 0, sizeof(list)); 26 while (scanf("%d%d%d%d", &n, &m, &b, &g) != EOF) { 27 int sex, val, ans = -1; 28 for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j){ 29 scanf("%d%d", &val, &sex); 30 sum[i][j] = sum[i][j-1] + sum[i-1][j] -sum[i-1][j-1] + val; 31 sboy[i][j] = sboy[i][j-1] + sboy[i-1][j] - sboy[i-1][j-1]; 32 if (sex == 1) sboy[i][j] ++; 33 int l = -1; 34 if (val >= 0) { 35 list[i][j] = list[i][j-1]; 36 for (int k = i; k; --k) { 37 l = max(l, list[k][j]); 38 solve(k, l+1, i, j); 39 if (sv > ans && sb >= b && sg >= g) 40 ans = sv; 41 } 42 } 43 else list[i][j] = j; 44 } 45 if (ans != -1) printf("%d\n", ans); 46 else printf("No solution!\n"); 47 } 48 return 0; 49 }