题目链接

问题描述

一个棋盘有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. Populating Next Right Pointers in Each Node leetcode java

    题目: Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode ...

  2. Create an Android library

    官方文档 创建 Android 库 [Create an Android library] Android 库在结构上与 Android app module 相同.它可以提供构建应用所需的一切内容, ...

  3. AI 经典书单 | 人工智能学习该读哪些书

    转载 2018年01月16日 00:00:00   人工智能相关岗位中,涉及到的内容包含: 算法.深度学习.机器学习.自然语言处理.数据结构.Tensorflow.Python .数据挖掘.搜索开发. ...

  4. 卡尔曼滤波算法--核心公式推导导论 - ZZ

    卡尔曼滤波算法--核心公式推导导论 10 个月前 写在最前面:这是我第一篇专栏文章,感谢知乎提供这么一个平台,让自己能和大家分享知识.本人会不定期的开始更新文章,文章的内容应该集中在汽车动力学控制,整 ...

  5. 【算法】Java-Redis-Hash算法对比-参考资料

    Java-Redis-Hash算法对比-参考资料 redis java map 红黑树_百度搜索 java使用redis缓存(String,bean,list,map) - CSDN博客 redis ...

  6. Structured Streaming编程向导

    简介 Structured Streaming is a scalable and fault-tolerant stream processing engine built on the Spark ...

  7. CSS drop down的一个很好的示例

    CSS drop down的一个很好的示例: http://www.runoob.com/css/css-dropdowns.html

  8. How to duplicate the records in a MongoDB collection

      // Fill out a literal array of collections you want to duplicate the records in. // Iterate over e ...

  9. IE DIV背景透明,点击事件不响应解决方案

    IE DIV背景透明,给DIV绑定点击事件, 当点击DIV时,不会响应已经绑定的点击事件. 解决方案是给DIV的设置以下样式. background-image: url(data:image/gif ...

  10. Android基础(五) Service全解析----看不见的Activity

    一.服务的介绍: 作为Android四大组件之中的一个,Service(服务)也常常运用于我们的日常使用中,它与Activity的差别在于:Service一直在后台执行.没实用户界面.所以绝不会到前台 ...