$[POJ1187]$ 陨石的秘密 题解

(POJ1187)

真是一个有趣的题目……我真是……唉……

我相信肯定有很多人拿到这道题会设如下的状态:

(f[l1][l2][l3][d])为有(l1)({ })(l2)([])(l3)(())时深度为(d)的个数,但是你想一下就会发现,不能转移!

因为你无法控制子状态中(d)的最大值,可怜~(@谁是鸽王)

于是就有这样一种神仙状态:(f[l1][l2][l3][d])为有(l1)({ }),(l2)([])(l3)(())时深度不超过(d)的个数!

(Woc),太妙了吧,这样我们就不用考虑最大值,只用用排列组合将子状态弄一起就行了!

具体转移:

考虑我们的当前串为两个部分,那么我们算出两个部分的答案后相乘即可,至于每个部分,我们枚举外面({ })([]),()的个数即可,这里要注意一下循环顺序,其他的问题就不大了。

上代码:

#include <iostream>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include <set>
using namespace std;
inline int read()
{
    int f=1,w=0;char x=0;
    while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
    while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
    return w*f;
}
const int p=11380;
int L1,L2,L3,D,f[20][20][30][40];
inline int Dfs(int l1,int l2,int l3,int d)
{
	int ans=0;
	if(!d&&(l1|l2|l3)) return 0;
	if(!(l1|l2|l3)) return f[l1][l2][l3][d]=1;
	if(f[l1][l2][l3][d]) return f[l1][l2][l3][d];
	for(int i=0;i<=l3;i++)
	{
		if(i) ans=(ans+Dfs(0,0,i-1,d-1)%p*Dfs(l1,l2,l3-i,d)%p)%p;
		for(int j=0;j<=l2;j++)
		{
			if(j) ans=(ans+Dfs(0,j-1,i,d-1)%p*Dfs(l1,l2-j,l3-i,d)%p)%p;
			for(int k=1;k<=l1;k++)
				ans=(ans+Dfs(k-1,j,i,d-1)%p*Dfs(l1-k,l2-j,l3-i,d)%p)%p;
		}
	}
	return f[l1][l2][l3][d]=ans;
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("Text1.in","r",stdin);
#endif
	L1=read(),L2=read(),L3=read(),D=read();
	Dfs(L1,L2,L3,D); if(D) Dfs(L1,L2,L3,D-1);
	int ans=f[L1][L2][L3][D]-(D?f[L1][L2][L3][D-1]:0);
	printf("%d",(ans+p)%p);
}

原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/10994068.html