1393 - Highways 计数问题

Hackerland is a happy democratic country with m×n cities, arranged in a rectangular m by ngrid and
connected by m roads in the east-west direction and n roads in the north-south direction. By public demand, this orthogonal road system is to be supplemented by a system of highways in sucha
way that there will be a direct connection between any pair of cities. Each highway is a straight line going through two or more cities. If two cities lie on the same highway, then they are directly connected.If two cities are in the same row or column, then
they are already connected by the existing orthogonal road system (each east-west road connects all the m cities in that row and each north-south road connects all the n cities in that column),
thus no new highway is needed to connect them. Your task is to count the number of highway that has to be built (a highway that goes through several cities on a straight line is counted as a single highway).

Input

The input contains several blocks of test cases. Each test case consists of a single line containing two integers 1n , m300 ,
specifying the number of cities. The input is terminated by a test case with n = m = 0 .

Output

For each test case, output one line containing a single integer, the number of highways that must be built.

Sample Input

2 4
3 3
0 0

Sample Output

12
14

本题是思想挺难的题目,抽象思维要求挺高的。

使用动态规划法,首先要弄明白如何记录数据,并且明白记录的数据代表什么意义,否则是理解不了的。

网上解析这道题的本来就不多,也许太难理解了,而解析的博客,我都没看明白,不知其所以言,有些是解析太简单了,也许作者是那样理解的,但是表达出来别人又没法理解了;

这里也只是给出自己的理解,也许别人不一定能理解我为什么这样理解的。不过给出图,详细说明一下,希望我可以说清楚。

作者 靖心:http://blog.csdn.net/kenden23/article/details/29185889

这里需要使用双重动态规划法了,首先定义第一个表tbl

这个表的数值,例如tbl[i][j]代表,从第一个点[1][1]到[i][j]点组成的(i-1) * (j-1)个方格,从点[1][1]出发想象发出射线到边界上所有点的总数。

如下图是i = 2, j = 2的时候有3条射线

如下图:

这样计算好一个表,为什么要这样记录数据呢? 因为比如我们的长方形是由n*m格组成的,我们记录所有的方格组成的到边界的射线,就可以通过查表得到所有线段数了。

上图的3*3方格包含了2*2个方格的子长方形,可以通过查表tbl[2][2]得到这个子长方形的射线数。

记录这个数据是这句代码:

for (int i = 1; i < N; i++)
		{
			for (int j = 1; j < N; j++)
			{
				//tbl[i][j]代表从顶点1,1出发的射线到边界的数量
				tbl[i][j] = tbl[i-1][j] + tbl[i][j-1] - tbl[i-1][j-1]
				+ (GCD(i, j) ==  1? 1 : 0);
			}
		}

下标其实是记录方格数的,并非记录顶点数。

然后因为一个大的N*M个方格包含了所有的子方格,所以需要把这些子方格都包含进去,计算总数才算是结果。

for (int i = 1; i < N; i++)
		{
			for (int j = 1; j < N; j++)
			{
				//从小到大逆转使用的所谓inclusion-exclusion原理
				rs[i][j] = rs[i-1][j] + rs[i][j-1] - rs[i-1][j-1]
				+ tbl[i][j] - tbl[i>>1][j>>1];
			}
		}

最后,为什么需要-tbl[i>>1][j>>1]呢?因为大家可以按照上图画画射线,就知道tbl[i][j]的边界射线和tbl[i>>1][j>>1]射线重合了。

其实tbl是一个对角线对称的矩阵,可以只计算对角线上面的值*2优化。

要仔细观察,总结规律:

1 大的方格包含的子方格,可以通过填表防止重复计算,十分难抽象的一个思维

2 只需要记录边界射线就可以了,内射线和子方格的值一样

最后AC程序,挺快的,可以上UVa榜:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;

class Highways1393
{
	static const int N = 302;
	vector<vector<int> > tbl;
	vector<vector<int> > rs;

	int GCD(int a, int b)
	{
		return b==0? a : GCD(b, a % b);
	}

	void makeTbl()
	{
		for (int i = 1; i < N; i++)
		{
			for (int j = 1; j < N; j++)
			{
				//tbl[i][j]代表从顶点1,1出发的射线到边界的数量
				tbl[i][j] = tbl[i-1][j] + tbl[i][j-1] - tbl[i-1][j-1]
				+ (GCD(i, j) ==  1? 1 : 0);
			}
		}

		for (int i = 1; i < N; i++)
		{
			for (int j = 1; j < N; j++)
			{
				//从小到大逆转使用的所谓inclusion-exclusion原理
				rs[i][j] = rs[i-1][j] + rs[i][j-1] - rs[i-1][j-1]
				+ tbl[i][j] - tbl[i>>1][j>>1];
			}
		}
	}

public:
	Highways1393() : tbl(N, vector<int>(N)), rs(N, vector<int>(N))
	{
		makeTbl();

		int r, c;
		while (scanf("%d %d", &r, &c) && (r != 0 && c != 0))
		{
			printf("%d\n", rs[r-1][c-1]<<1);
		}
	}
};

1393 - Highways 计数问题

时间: 2024-10-09 09:09:21

1393 - Highways 计数问题的相关文章

UVA - 1393 Highways

Description Hackerland is a happy democratic country with m×n cities, arranged in a rectangular m by n grid and connected by m roads in the east-west direction and n roads in the north-south direction. By public demand, this orthogonal road system is

uva 1393 - Highways(容斥原理)

题目连接:uva 1393 - Highways 题目大意:给定一个m?n的矩阵,将矩阵上的点两两相连,问有多少条直线至少经过两点. 解题思路:头一次做这样的题目,卡了一晚上. dp[i][j]即为i?j的矩阵中有多少条红色的线,然后最后答案*2,即水平翻转下蓝色的线.非常easy发现,全部的线都过ij互质的点(即最大公约数为1).然后用容斥原理求出ans. #include <cstdio> #include <cstring> const int N = 305; int n,

UVA 1393 - Highways (容斥原理计数)

题目链接:1393 - Highways 题意:给定一个n * m的点阵,问两两相连后,能组成多少条至少穿过两个点的直线,并且不是水平或垂直的 思路:找过两点的线段,由于是整数坐标,只要他的斜率不是整数,即x / y不是整数就能满足答案,然后先记录下这所有的位置,然后利用容斥原理求出对应每个点可以连出多少条这样的线段,最后去求和,求和的时候要注意,由于有一些是重复计算了,比如1 1 和 2 2 连,2 2 和 3 3 连,这样其实是算一条的,所以最后在求和的时候要扣掉重复的部分,直接减去sum[

UVa 1393 Highways (动态规划)

题目 题目大意 有一个\(n\)行\(m\)列(\(1 ≤ n, m ≤ 300\))的点阵, 问: 一共有多少条非水平非竖直的直线至少穿过其中两个点? 例如, \(n = 2\), \(m = 4\)时答案为\(12\), \(n = m = 3\)时答案为\(14\). 题解 一开始看到题目我立马想到了\(SPOJ 104 Highways\)(基尔霍夫矩阵-树定理), 然而本题跟这个定理完全没有关系. 首先考虑如何判断是否形成一条之前没有出现过的直线, 如果它向量两坐标的最大公约数为\(1

UVA 1393 Highways(数学思想)

题意:给你n.m(n,m<=200),问你有多少条非水平.非垂直的直线有多少条经过至少两个点 题解:我们需要枚举的是只画一条线的矩形,对于大小a*b的矩形必须保证gcd(a,b)=1才能不重复 接着对于每个矩形可以放的位置有(n-a)(m-b)个,但是如果有矩形在某个矩形的左上方就会再次重复 因此只需要再减去max(0,n-2*a)*max(0,m-2*b)就好 import java.util.Scanner; public class Main{ static int Max=305; st

UVA 1393 Highways,UVA 12075 Counting Triangles —— (组合数,dp)

先看第一题,有n*m个点,求在这些点中,有多少条直线,经过了至少两点,且不是水平的也不是竖直的. 分析:由于对称性,我们只要求一个方向的线即可.该题分成两个过程,第一个过程是求出n*m的矩形中,dp[i][j]代表在这个矩形中终点是到(i,j)这个点的满足题意的直线条数,那么,用dp的话就可以得出递推关系:由长和宽分别小1的左右两个矩形中满足题意的线的条数减去他们共有的矩形中满足的线的条数(容斥减去重复部分),之后还要判断从最左上角的点(1,1)到(i,j)是否可以组成一条线,这个条件是gcd(

1393 - Highways(问题抽象)(容斥原理计数)

问题的方向是对称的,统计\*2即可,当gcd(a,b)>1及重复, 证:a*b满足gcd(a,b)>1,在他对角线和a'和b'对角线是同一条直线(a'=a/gcd(a,b)b'=b/gcd(a,b)) 其次,如果放置位置不够靠左,也不够靠上,则它和它"左上方"的包围盒也重复了 假定左上角坐标(0,0)则对于左上角在(x,y)的包围盒,其左上方的包围盒的左上角为(x-a,y-b),这个左上角合法条件是x-a>=0,y-b>=0 包围盒本身不出界的条件是x+a<

UVa 1393 (容斥原理、GCD) Highways

题意: 给出一个n行m列的点阵,求共有多少条非水平非竖直线至少经过其中两点. 分析: 首先说紫书上的思路,编程较简单且容易理解.由于对称性,所以只统计“\”这种线型的,最后乘2即是答案. 枚举斜线包围盒的大小,如果盒子的长宽ab互质,则是可以的.这种盒子共有(m-a)(n-b)个,但要减去其中重复的.如果有一个长宽为2a和2b的大盒子,则计数右下角的小盒子的同时,左上角的小盒子会重复,所以要减去重复的盒子的个数c = max(0, m-2a) * max(0, n-2b) 最后gcd(a, b)

UVA - 12075 Counting Triangles

Description Triangles are polygons with three sides and strictly positive area. Lattice triangles are the triangles all whose vertexes have integer coordinates. In this problem you have to find the number of lattice triangles in anMxN grid. For examp