ZOJ Monthly, June 2012 - F Choir III - zoj 3616

题目说找一个 子矩阵,里面价值最大,其中男生人数 >= b, 女生人数 >= g, 中间不能有不会唱歌的人。

这题如果关键在于 排除不会唱歌的人,思想肯定是贪心思想,想人数越多越好,男生女生不过就是在if里面多判定的一句话罢了。

为了方便理解,我画张很丑的图...

蓝色表示我当前扫到的队员,当然他是会唱歌的。

红色表示不会唱歌的队员。

白色表示,没选择的队员。

我在扫描时,对于每一个点是一行行扫,这样的话,复杂度是 N*M*N。

先扫第当前行,也就是第4行,因为第四行上面之前的队员我都会唱歌,那我尽量全部选走。

粉色代表这次选择的队员。

再上一行,因为有不会唱歌的队员,那我只能放弃第一列之前的所有成员。

同理在上一行是:

最后因为不会唱歌的队员和蓝色点成员在一列了,也就是说在这列之前是不会选成员了,即:

扫描结束,另外其中对于子矩阵的能力值的和可以在一开始预处理掉,后面的询问就是O(1)了。

我在代码中用list[i][j] 表示当前i行,第j个队员之前离j最近的不会唱歌的队员编号。

View Code
 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 }

转载于:https://www.cnblogs.com/slon/archive/2012/06/30/2571084.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值