1. 程式人生 > >區間DP 等腰三角形

區間DP 等腰三角形

題目描述:給定一個正N邊形,可以通過連線將這個多邊形分割成N-2個三角形,問這N-2個三角形中恰有k個等腰三角形的分割方法有多少?這個值可能很大,輸出對9397取模的結果。
資料範圍:n,k <= 50.

這道題也是區間DP,不過稍微難一點。

首先我們先想個辦法判斷等腰三角形,因為這是一個正多邊形,所以我們對於三個點,我們可以計算一下他們的差的絕對值,直接比較這個是否相同即可。

之後就是怎麼DP了,想到剛才的三角劃分,這題應該也是一道區間DP。令dp[i][j][k]表示在區間i~j之內劃分出k個等腰三角形的方案數,之後我們列舉一下端點,判斷一下新的斷點能否形成等腰三角形進行轉移。

這樣的複雜度是O(n3*k2)的,會超時,我們考慮優化。因為這是一個正多邊形,所以我們可以直接用dp[i][j]表示把以i個連續點為頂點的正多邊形劃分出j個等腰三角形的方案數。之後直接列舉從幾個連續點的位置斷開進行轉移即可,這樣複雜度被優化到了O(n2

k2),可以過。

如果不大理解的話,可以結合凸多邊形三角劃分這道題想一想。

看一下程式碼。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define
enter putchar('\n') using namespace std; typedef long long ll; const int M = 10005; const ll INF = 1000000009; const int mod = 9397; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '
0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int n,k,dp[105][105]; bool judge(int x,int m) { int ta = min(x - 1,n - x + 1),tb = min(m - 1,n - m + 1),tc = min(m - x,n - m + x); return (ta == tb || tb == tc || ta == tc); } int dfs(int n,int k) { if(dp[n][k] != -1) return dp[n][k]; if(n <= 2) return 0; int cur = 0; rep(x,2,n-1) { rep(j,0,k-judge(x,n)) cur += dfs(x,j) * dfs(n-x+1,k-j-judge(x,n)),cur %= mod; } return dp[n][k] = cur; } int main() { n = read(),k = read(); memset(dp,-1,sizeof(dp)); dp[2][0] = 1; printf("%d\n",dfs(n,k)); return 0; }