ACM模版
描述
题解
这个题的水真深,不得不说这个题的解题思路有些始料未及……
首先通过管道铺设的规则我们可以知道,管道最多只能有两个弯,而且管道口只能再非顶点边缘上,这不免有些让人感觉到复杂,但是也正是这个告诉我们这个题可以转化为求三类折线,贯穿线、折一弯儿端点在临边线、折两弯儿端点在对边线。
于是我们将问题转换为了折点的问题,根据不同折点可以构成的折法,可以求出来我们需要的解。
所以我们可以先求左右方向的情况,再求上下方向的情况,这里将 map 倒置了一下,上下方向的情况按照左右算。
这三类折线中,除了第二类外,其他两类不易重复,所以我们可以将这一类特别考虑,只在第一次左右方向查找时处理一下,由于代码的逻辑原因,这样处理后虽然第二类不重复了,但是第一类少了上下方向的贯穿线,于是在求第一次时,顺便求出纵向贯穿线即可。
具体还是仔细看代码吧,这个代码是我在参考了 rank1 的大神的代码后改写的(实际上也没改嘛子),注释十分详尽,希望可以一目了然……
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN =
2005;
int n, m;
char S[MAXN];
bool A[MAXN][MAXN], B[MAXN][MAXN];
bool D[MAXN][MAXN], U[MAXN][MAXN];
long long ans =
0;
void solve(
bool T[][MAXN],
int n,
int m,
int flag)
{
memset(D,
0,
sizeof(D));
memset(U,
0,
sizeof(U));
for (
int i =
1; i <= n; i++)
{
for (
int j =
1; j <= m; j++)
{
D[i][j] = D[i -
1][j] | T[i][j];
}
}
for (
int i = n; i; i--)
{
for (
int j =
1; j <= m; j++)
{
U[i][j] = U[i +
1][j] | T[i][j];
}
}
for (
int i =
2; i < n; i++)
{
int tmp =
0;
if (!T[i][
1] && flag)
{
tmp =
1;
}
D[i][
1] = U[i][
1] =
1;
for (
int j =
2; j < m; j++)
{
if (T[i][j])
{
tmp =
0;
continue;
}
ans += ((!D[i][j]) + (!U[i][j])) * tmp;
ans += (!D[i][j] && !U[i][j -
1]) + (!U[i][j] && !D[i][j -
1]);
tmp += (!D[i][j -
1]) + (!U[i][j -
1]);
}
if (!T[i][m] && flag)
{
ans += tmp + (!D[i][m -
1]) + (!U[i][m -
1]);
}
}
if (flag)
{
for (
int j =
2; j < m; j++)
{
ans += (!D[n][j]);
}
}
}
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++)
{
A[i][j] = S[j] ==
'#';
}
}
solve(A, n, m,
1);
for (
int i =
1; i <= n; i++)
{
for (
int j =
1; j <= m; j++)
{
B[j][i] = A[i][j];
}
}
solve(B, m, n,
0);
printf(
"%lld\n", ans);
return 0;
}