JOISC/2018
JOISC18 Tents
arnold518
2020. 10. 12. 09:36
D문제
oj.uz/problem/view/JOI18_tents
문제 보기 - Tents (JOI18_tents) :: oj.uz
문제 보기 - Tents (JOI18_tents)
oj.uz
N*M의 격자에 텐트를 배치하는데, 각 텐트별 동, 서, 남, 북의 4가지 방향이 있다. 어떤 두 텐트가 같은 행이나 열에 있다면 이 두 텐트는 서로 마주보고 있는 방향이어야 한다. 이 조건들을 모두 만족시킬 때 전체 텐트의 배치로 가능한 경우의 수를 구하자.
$N, M<=3000$
풀이
Observation 1 : 한 행이나 열에 위치할 수 있는 텐트는 최대 2개이다.
만약 한 행이나 열에 텐트가 3개 이상 배치된다면, 각 텐트를 서로 마주보게 할 수 없다. 따라서 어떠한 행이나 열에 위치할 수 있는 텐트는 0, 1, 2개이다.
DP를 통해 해결하자.
1. 첫번째 행에 2개의 텐트가 있다.
텐트의 방향은 고정이고, 위치를 정해준다. 그 후, 해당하는 두 텐트가 위치한 두 열은 다른 텐트들이 있을 수 없기 때문에 제거해준다.
2. 첫번째 행에 1개의 텐트가 있으며, 1개의 텐트가 있는 열에 다른 텐트가 하나 있다.
1과 같은 논리로 해당 두 텐트의 위치를 결정해준 후, 다른 텐트들이 위치할 수 없는 두 행을 제거해준다.
3. 첫번째 행에 1개의 텐트가 있으며, 1개의 텐트가 있는 열에 다른 텐트가 없다.
텐트의 위치와 방향을 모두 결정하고, 그 행과 열을 제거한다.
4. 첫번째 행에 아무 텐트가 없다.
그 행을 제거한다.
시간 복잡도 : $O(NM)$
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAXN = 3000;
const ll MOD = 1e9+7;
int N, M;
ll dp[MAXN+10][MAXN+10];
int main()
{
scanf("%d%d", &N, &M);
for(int i=0; i<=N; i++) dp[i][0]=1;
for(int j=0; j<=M; j++) dp[0][j]=1;
for(int i=1; i<=N; i++)
{
for(int j=1; j<=M; j++)
{
if(j>=2) dp[i][j]+=(ll)j*(j-1)/2*dp[i-1][j-2]%MOD;
dp[i][j]+=(ll)4*j*dp[i-1][j-1]%MOD;
if(i>=2) dp[i][j]+=(ll)j*(i-1)*dp[i-2][j-1]%MOD;
dp[i][j]+=dp[i-1][j];
dp[i][j]%=MOD;
}
}
printf("%lld\n", (dp[N][M]+MOD-1)%MOD);
}