UVa 1393 Highways (动态规划)
Posted forth
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVa 1393 Highways (动态规划)相关的知识,希望对你有一定的参考价值。
题目
题目大意
有一个(n)行(m)列((1 ≤ n, m ≤ 300))的点阵, 问: 一共有多少条非水平非竖直的直线至少穿过其中两个点? 例如, (n = 2), (m = 4)时答案为(12), (n = m = 3)时答案为(14)。
题解
一开始看到题目我立马想到了(SPOJ 104 Highways)(基尔霍夫矩阵-树定理), 然而本题跟这个定理完全没有关系。
首先考虑如何判断是否形成一条之前没有出现过的直线, 如果它向量两坐标的最大公约数为(1)则没有出现过。我们考虑用(dp_{i j})表示表示向量((x, y))((x ≤ i), (y ≤ j))共有多少个((x, y) = 1)的, 递推式为:
[dp_{i j} = dp_{i - 1 j} + dp_{i j - 1} - dp_{i - 1 j - 1} ((i, j) ≠ 1)]
[dp_{i j} = dp_{i - 1 j} + dp_{i j - 1} - dp_{i - 1 j - 1} + 1 ((i, j) = 1)]
接下来处理所有点, 到((i, j))时, 内部各点与他组成的向量范围都在({(x, y)|x∈[1, i), y∈[1, j), x, y∈N^*})内, 那么可以用(dp_{i - 1 j - 1})表示其中有多少与其互质, 再减去重复计算的((dp_{frac{i - 1}{2} frac{j - 1}{2}}))。为了方便我们将所有(i), (j)增加(1), 最后减(1)即可。
代码
#include <cstdio>
long long ans[310][310], dp[310][310];
int n, m;
inline long long GreatestCommonDivisor(const long long&, const long long&);
int main(int argc, char const *argv[]) {
for (register long long i(1); i <= 300ll; ++i) {
for (register long long j(1); j <= 300ll; ++j) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + (GreatestCommonDivisor(i, j) == 1);
ans[i][j] = ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1] + dp[i][j] - dp[i >> 1][j >> 1];
}
}
while (~scanf("%d %d", &n, &m) && (n || m)) {
printf("%lld
", ans[n - 1][m - 1] << 1);
}
return 0;
}
inline long long GreatestCommonDivisor(const long long &a, const long long &b) {
return b ? GreatestCommonDivisor(b, a % b) : a;
}
以上是关于UVa 1393 Highways (动态规划)的主要内容,如果未能解决你的问题,请参考以下文章