hdu5396 2015多校第九場 區間dp

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5396


題意:給你n個數和n-1個操作符(2<=n<=100),問你不斷地每次給操作符的左右相鄰的兩個數加括號,直到最後只剩一個數爲止,問你不同的方案的所有最終剩下一個數的和是多少。在一次取操作符時只要操作符的位置不相同就算一種方案。如果有n個操作符的話肯定有n!種方案。


思路:看到n比較小。。。不是搜索就是區間dp。。。顯然,搜索是n!種方案,算到量子計算機被研製出來都算不出來。。。那肯定區間dp了。。既然是dp,那麼狀態方程和轉移明瞭了,這道題就可解了。。。

設dp[i][j]爲第i個數到第j個數的所有方案的和,那麼假設我們已經枚舉到區間i,j(i < j),對於加法和減法這兩種情況是類似的,我們考慮第k個操作符(i<=k<j,假設k這個操作符是最後一次做的,兩邊的操作符都已經完成了),有

dp[i][j] += (dp[i][k] * A[j - (k + 1)] % mod + dp[k + 1][j] * A[k - i] % mod) % mod * C[j - i - 1][k - i] % mod

其中A數組儲存階乘值,C數組儲存組合值。

爲什麼要乘以階乘?對於左邊的i到k來說,假設右邊有n個符號,那麼右邊肯定有n!種方案,對於每個方案都要加上左邊的總和,所以要乘以一個階乘。同理右邊。。。。

爲什麼要乘以一個組合數?因爲第k個操作符是最後取的,那麼假設左邊有n個操作符,右邊有m個操作符,這麼操作符取的順序不同,最後的方案數也是不同的,所以我們把n+m個看作是取的順序的位置,C[n + m][n]就相當與左邊那些操作符放在哪些順序的位置上。。。。


乘法的轉移方程稍微有些不同,

dp[i][j] += (dp[i][k] * dp[k + 1][j] % mod) * C[j - i - 1][k - i] % mod就行了



代碼:

#include 
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 150;
const ll mod = 1e9 + 7;

string op;
ll ori[maxn];
ll dp[maxn][maxn];
string s;
ll A[maxn];
ll C[maxn][maxn];
int n;

void pre()
{
	C[1][0] = C[1][1] = 1LL;
	C[0][0] = C[0][1] = 1LL;
	A[0] = A[1] = 1;
	for(int i = 1;i <= 102;i++)
	{
		C[i][0] = C[i][i] = 1LL;
	}
	for(ll i = 2;i <= 102;i++)
	{
		A[i] = A[i - 1] * i % mod;
		for(int j = 1;j < i;j++)
		C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
	}
}

int main()
{
	pre();
	while(~scanf("%d",&n))
	{
		mem(dp,0);
		for(int i = 1;i <= n;i++){
			scanf("%lld",ori + i);dp[i][i] = ori[i];
		}
		cin>>s;
		op = "(";
		op += s;
		for(int i = n;i >= 1;i--)
		{
			for(int j = i + 1;j <= n;j++)
			{
				for(int k = i;k < j;k++)
				{
					if(op[k] == '+'){
						dp[i][j] += (dp[i][k] * A[j - (k + 1)] % mod + dp[k + 1][j] * A[k - i] % mod) % mod * C[j - i - 1][k - i] % mod;
						dp[i][j] %= mod;
					}else if(op[k] == '-'){
						dp[i][j] += (dp[i][k] * A[j - (k + 1)] - dp[k + 1][j] * A[k - i]) % mod * C[j - i - 1][k - i] % mod;
						dp[i][j] %= mod;
						dp[i][j] %= mod;
					}else if(op[k] == '*'){
						dp[i][j] += (dp[i][k] * dp[k + 1][j] % mod) * C[j - i - 1][k - i] % mod;
						dp[i][j] += mod;
						dp[i][j] %= mod;
					}
				}
			}
		}
		while(dp[1][n] < 0)//可能小於0的。。不加會wa
			dp[1][n] += mod;
		printf("%lld\n",dp[1][n] % mod);
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章