题目链接

问题描述

一个棋盘有n条横线,m条竖线,上面有k个黑点,问有多少个不包含黑点的矩形。

数据范围:

n和m最大为1000,k最大为10

方法一:动态规划

复杂度n*m*k

import java.awt.Point;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner; public class Main {
int n, m;
long dp[][];
List<Point> black; long solve() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
int now = 0;//now表示以i,j这个点为右下角的矩形的个数
int lastX = 0;
int lastY = j;
for (int k = black.size() - 1; k >= -1; k--) {
//此处哨兵单元设计很巧妙,让k==-1的时候自动启用哨兵单元
Point p;
if (k == -1) p = new Point(i, 0);
else p = black.get(k);
if (p.y > j) continue;
if (p.x > i || p.x < lastX) continue;
now += (i - lastX) * (lastY - p.y);
lastX = p.x;
lastY = p.y;
}
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + now;
}
}
return dp[n][m];
} Main() {
Scanner cin = new Scanner(System.in);
n = cin.nextInt();
m = cin.nextInt();
dp = new long[n + 1][m + 1];
black = new LinkedList<>();
int k = cin.nextInt();
while (k-- > 0) {
int r = cin.nextInt(), c = cin.nextInt();
black.add(new Point(r, c));
}
//对全部点进行排序(按照列从小到大进行排序)
black.sort(Comparator.comparing(x -> x.y));
for (int i = 0; i <= n; i++) dp[i][0] = 0;
for (int i = 0; i <= m; i++) dp[0][i] = 0;
long ans = solve();
System.out.println(ans);
} public static void main(String[] args) {
new Main();
}
}

可以通过“缩点”法降低复杂度。

方法二:容斥原理

10个点有1024种情况,判断每种情况是否合理,然后统计个数即可。

复杂度之和k有关,跟n、m无关。

相似问题

codeforce上有一道规模更大的题,n和m为10000,k为20:

http://codeforces.com/gym/101350/problem/G

51nod

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1291

#include<bits/stdc++.h>
using namespace std;
#define st first
#define nd second
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define sz(x) (int)x.size()
#define de(x) cout<< #x<<" = "<<x<<endl
#define dd(x) cout<< #x<<" = "<<x<<" "
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi; const int N = 666;
int n, m, top;
int u[N], sta[N];
ll c[N][N];
char s[N]; int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%s", s+1);
for(int j = 1; j <= m; j++)
u[j] = (s[j] == '1')? u[j]+1: 0;
top = 0;
sta[top++]=0;
for(int j = 1; j <= m+1; j++) {
while(u[sta[top-1]] > u[j]) {
++c[max(u[sta[top-2]], u[j])+1][j-sta[top-2]-1];//维护单调上升的栈, 看每个柱块向左和向右的最大延伸距离, 即为宽度
--c[u[sta[top-1]]+1][j-sta[top-2]-1]; //枚举i为底边, 对高度范围为[max(u[sta[top-2]],u[j])+1, u[sta[top-1]]], 宽度为j-sta[top-2]-1的矩形加1
--top;
}
while(top && u[sta[top-1]] == u[j]) --top;
sta[top++] = j;
}
}
for(int i = 2; i <= n; i++) for(int j = 1; j <= m; j++) c[i][j] += c[i-1][j]; //c1[i, j]: 高为i (n^2) 的连通块, 只统计最长宽度的。
for(int i = 1; i <= n; i++) {
for(int j = m-1; j; j--) c[i][j] += c[i][j+1]; //c2[i, j]: 高i宽 >= j的连通块的个数
for(int j = m-1; j; j--) c[i][j] += c[i][j+1]; //c3[i, j]: 高i宽j的全1矩阵的个数
}
for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) printf("%lld%c", c[i][j], " \n"[j == m]);
return 0;
}
/*
3 3
011
110
110
如何得到c1? 暴力的话, c1是枚举n^2的上下边界, 优化后, 变成枚举直方图的最底边,快速统计各个不同的上顶边。这个可以通过单调栈+差分解决。求前缀和后就得到c1。
总的时间复杂度为O(nm).
c1
0 3 0
1 1 0
1 0 0
c2
3 3 0
2 1 0
1 0 0
c3
6 3 0
3 1 0
1 0 0 */

leetcode

https://www.nowcoder.com/acm/contest/79/D

#include<bits/stdc++.h>
using namespace std;
#define st first
#define nd second
typedef long long ll;
const int mod = 1e9+7;
pair<int, int> s[5111];
int main() {
int n, m, c;
scanf("%d%d%d", &n, &m, &c);
for(int i = 1, x, y; i <= c; i++) scanf("%d%d", &x, &y), s[i] = {x, y};
sort(s+1, s+c+1);
long long ans = 0;
for(int i = 1; i <= c; i++){
int l = 0, r = m+1;
for(int j = i-1; ~j; j--){ //从下往上从左往右枚举之前的点
ans += (r-s[i].nd)*(n-s[i].st+1LL)%mod*(s[j+1].st-s[j].st)%mod*(s[i].nd-l)%mod; //上边界为s[j].st ~ s[j+1].st
ans %= mod;
if(s[j].nd > s[i].nd) r = min(r, s[j].nd);
if(s[j].nd < s[i].nd) l = max(l, s[j].nd);
}
}
//n*m矩阵的子矩阵个数 = 左右两边界 * 上下两边界
long long cnt = (n*(n+1LL)/2%mod)*(m*(m+1LL)/2%mod)%mod;
ans = (cnt-ans)%mod;
if(ans < 0) ans += mod;
printf("%lld\n", ans);
return 0;
}

参考资料

https://www.cnblogs.com/dirge/p/10034268.html

hihocoder234周 计算不包含黑点的矩形个数的更多相关文章

  1. 【Java】一个小程序,计算它包含的代码所需的耗时

    写一个小程序,用来计算它包含的代码所需的耗时.虽然简单,测试代码是否耗时还是有点用的,不用重新写嘛~ import java.util.Date; import java.util.concurren ...

  2. 计算1到N中包含数字1的个数

    转自:http://pandonix.iteye.com/blog/204840 Mark N为正整数,计算从1到N的所有整数中包含数字1的个数.比如,N=10,从1,2...10,包含有2个数字1. ...

  3. hdu2524 (求矩形个数,水题。。。)

    hdu 2524 N - 暴力求解.打表 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64 ...

  4. Java实现 LeetCode 315 计算右侧小于当前元素的个数

    315. 计算右侧小于当前元素的个数 给定一个整数数组 nums,按要求返回一个新数组 counts.数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i ...

  5. 计算阶乘n!末尾0的个数

    一.问题描述 给定一个正整数n,请计算n的阶乘n!末尾所含有“0”的个数.例如: 5!=120,其末尾所含有的“0”的个数为1: 10!= 3628800,其末尾所含有的“0”的个数为2: 20!= ...

  6. 基于visual Studio2013解决面试题之0410计算二进制中1的个数

     题目

  7. Java:判断字符串中包含某字符的个数

    Java:判断字符串中包含某字符的个数 JAVA中查询一个词在内容中出现的次数: public int getCount(String str,String key){ if(str == null ...

  8. Leetcode 357.计算各个位数不同的数字个数

    计算各个位数不同的数字个数 给定一个非负整数 n,计算各位数字都不同的数字 x 的个数,其中 0 ≤ x < 10n . 示例: 输入: 2 输出: 91 解释: 答案应为除去 11,22,33 ...

  9. Leetcode 315.计算右侧小于当前元素的个数

    计算右侧小于当前元素的个数 给定一个整数数组 nums,按要求返回一个新数组 counts.数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元 ...

随机推荐

  1. JSTL fn:contains()函数

    fn:contains() 函数判断一个输入字符串是否包含一个指定的子串. 语法 使用 fn:contains() 函数具有以下语法: boolean contains(java.lang.Strin ...

  2. window.location属性用法及解决一个window.location.search为什么为空的问题

    通常用window.location该属性获取页面 URL 地址: 1.什么是window.location? 比如URL:http://b.a.com:88/index.php?name=kang& ...

  3. 使用numba加速python程序

    前面说过使用Cython来加速python程序的运行速度,但是相对来说程序改动较大,这次就说一种简单的方式来加速python计算速度的方法,就是使用numba库来进行,numba库可以使用JIT技术即 ...

  4. tail -f 然后grep,处理缓存的问题

    学习了:http://www.quwenqing.com/read-134.html 对日志记录做多次grep过滤输出,格式如下: tail -f log | grep xxx | grep yyy ...

  5. C 语言宏定义

    C 语言宏定义1.例子如下: #define PRINT_STR(s) printf("%s",s.c_str()) string str = "abcd"; ...

  6. Android 关于操作栏 ActionBar 的设计原则【转载+整理】

    原文地址 本文内容 操作栏目的 基本布局 适应旋转和不同的屏幕尺寸 副操作栏的布局 操作栏按钮 上下文操作栏 操作栏清单 设计原则就是为你在编写 Android APP 时,尤其是如何安排操作按钮的位 ...

  7. 坑人无数的Redis面试题

    https://baijiahao.baidu.com/s?id=1588454565071211950&wfr=spider&for=pc 坑人无数的Redis面试题

  8. 牛客网-《剑指offer》-二进制中1的个数

    题目:http://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8 C++ 负数需要特殊处理,因为负数右移会补1(符号位) cla ...

  9. 通过httpclient3实现文件下载以及获取文件下载名称

    package httpclient3test; import java.io.File; import java.io.FileNotFoundException; import java.io.F ...

  10. 前台登录和Token信息交互流程

    原来总是对前台登录,怎么利用token有点迷惑,后面仔细的想了一遍,把自己简单的想法记录下来,留作记录,以便后续优化 各路大神有什么看法也可以说,能更完善整个流程. 不说了,暴力的上图: 该图是出自c ...