ZOJ 3616 Choir III 最大权子矩阵
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3616
题意:
给一个n*m的矩阵,不能选负数,要求一个子矩阵,使得该子矩阵所有值加起来最大。还有一个限制,矩阵元素有1和2两类,要求所选子矩阵两类元素数目各不少于一个值。
分析:
就是最大权子矩阵。以前学的,好久没写这类题,也忘得差不多了,推了一会想起了一个o(n^2m)的做法。先对每列做前缀和的预处理,然后枚举两行,依靠前缀和可以o(1)得到某列这两行之间元素的和,也就是把这两行之间都压缩了,然后就是一维的一个最大子段和问题,可以o(n)地做,即一直累加,小于0就重置为0。不能选负数就把负数设成负无穷,另一个限制只要统计一下就可以,如果满足了再尝试更新答案。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstdlib> 7 #include<set> 8 #include<map> 9 #include<queue> 10 #include<ctime> 11 #include<string> 12 using namespace std; 13 14 typedef long long LL; 15 const LL inf = 847483647ll; 16 17 struct node{ 18 LL v; 19 int t; 20 } a[110][2100]; 21 LL sum[2100][110], boy[2100][110], girl[2100][110]; 22 23 int n, m, b, g; 24 int main() 25 { 26 while(scanf("%d %d %d %d", &n, &m, &b, &g) != EOF) 27 { 28 for (int i = 1; i <= n; i++) 29 for (int j = 1; j <= m; j++){ 30 scanf("%lld %d", &a[i][j].v, &a[i][j].t); 31 if (a[i][j].v < 0) a[i][j].v = -inf; 32 } 33 for (int i = 1; i <= m; i++){ 34 sum[i][0] = boy[i][0] = girl[i][0] = 0; 35 for (int j = 1; j <= n; j++){ 36 sum[i][j] = sum[i][j-1] + a[j][i].v; 37 boy[i][j] = boy[i][j-1]; 38 girl[i][j] = girl[i][j-1]; 39 if (a[j][i].t == 1) boy[i][j]++; 40 else girl[i][j] ++; 41 } 42 } 43 LL ans = -1; 44 LL cursum, curb, curg; 45 for (int i = 1; i <= n; i++){ 46 for (int j = i; j <= n; j++){ 47 cursum = curb = curg = 0; 48 for (int k = 1; k <= m; k++){ 49 cursum = cursum + sum[k][j] - sum[k][i-1]; 50 curb = curb + boy[k][j] - boy[k][i-1]; 51 curg = curg + girl[k][j] - girl[k][i-1]; 52 if (cursum < 0){ 53 cursum = curb = curg = 0; 54 } 55 else{ 56 if (curb >= b && curg >= g) ans = max(ans, cursum); 57 } 58 } 59 } 60 } 61 if (ans == -1) puts("No solution!"); 62 else printf("%lld\n", ans); 63 } 64 return 0; 65 }