题目链接

问题描述

一个棋盘有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. 杨晓峰-Java核心技术-6 动态代理 反射 MD

    目录 第6讲 | 动态代理是基于什么原理? 典型回答 考点分析 知识扩展 反射机制及其演进 动态代理 精选留言 Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAnd ...

  2. [总结]Jquery api 快速参考

    选择符 匹配 * 所有元素 #id 带有给定ID的元素 element 给定类型的所有元素,比如说html标签 .class 带有给定类的所有元素 a,b 匹配a或者匹配b的元素 a b 作为a后代的 ...

  3. 【Spark】SparkStreaming-Tasks-数量如何设置?

    SparkStreaming-Tasks-数量如何设置? sparkstreaming task 数量设置_百度搜索 spark内核揭秘-14-Spark性能优化的10大问题及其解决方案 - star ...

  4. Mono Touch Table应用

    , UIApplication.SharedApplication.StatusBarFrame.Height                         , UIScreen.MainScree ...

  5. 滴滴大数据算法大赛Di-Tech2016参赛总结

    https://www.jianshu.com/p/4140be00d4e3 题目描述 建模方法 特征工程 我的几次提升方法 从其他队伍那里学习到的提升方法 总结和感想 神经网络方法的一点思考 大数据 ...

  6. PHP开发框架比较

    PHP开发框架比较 Laravel 是一个简单优雅的 PHP WEB 开发框架,将你从意大利面条式的代码中解放出来.通过简单.优雅.表达式语法开发出很棒的 WEB应用!但是通过使用我们发现Larave ...

  7. editplus教程

    Editplus 3.0 开发ext 教程 使用Editplus配置轻型的PHP调试环境 Editplus已经使用了很多年,一直很喜欢这个小巧.精致的文本编辑器,比起ZenStudio这样庞大的IDE ...

  8. 关于Selenium Chrome Driver相关的一些资源

    这里摘录一些处理所需要的jar包,以及对照关系等. 参考: selenium-chrome-driver-2.22.0.jar:http://www.java2s.com/Code/Jar/s/Dow ...

  9. hibernate update 只更新部分字段的3种方法(其实我只想说第二种)

    hibernate 中如果直接使用Session.update(Object o);会把这个表中的所有字段更新一遍. 比如: public class Teacher Test { @Test pub ...

  10. Mina.Net实现的断线重连

    using Mina.Filter.Codec; using Mina.Filter.Codec.TextLine; using System; using System.Collections.Ge ...