*题解:P6701 [POI 1997] Genotype

原题链接

解析

首先可以想到区间 dp。

\(f_{l,r}\) 表示分裂出 \(T[l,r]\) 所需的最少 \(\texttt{S}\) 个数,其中 \(T\) 是目标串。

但是仔细思考后发现根本没有办法转移,于是倒闭。

正难则反,考虑怎么合成。

\(f_{l,r}\) 表示合成 \(T[l,r]\) 中的字符后得到的最少 \(\texttt{S}\) 个数。

但是我们发现这个问题一点都不“子问题”,因为合成是多层的,并且合成方案还不唯一。我们需要一个更加简单的子问题。

\(f_{l,r}\) 表示将 \(T[l,r]\) 中的字母合成一个字母,可以合成出的字母的集合。由于字母只有 \(26\) 个,所以可以状压。

这样是好转移的:

\[f_{l,r}= \bigcup_{k=l}^{r-1}\operatorname{calc}(f_{l,k},f_{k + 1,r}) \]

其中 \(\operatorname{calc}\) 求的是两边集合中的字符可以合成出的字符集合,可以在 \(O(|\Sigma|^2)\) 的时间内求出,\(|\Sigma|\) 为字符集大小。

对于统计答案,可以使用我们之前弃用的状态。设 \(g_{l,r}\) 表示合成 \(T[l,r]\) 中的字符后得到的最少 \(\texttt{S}\) 个数。

那么,若 \(\texttt{S} \in f_{l,r}\)

\[g_{l,r}=1 \]

否则:

\[g_{l,r}=\min_{k=l}^{r-1}(g_{l,k} + g_{k + 1,r}) \]

\(g_{1,len}\) 即为所求。

于是,我们在 \(O(|\Sigma|^2\cdot L^3\cdot k)\) 的时间内解决了这个问题。

代码

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N = 26 + 5,M = 100 + 5;
int can[N][N],f[M][M],g[M][M];
int calc(int a,int b){
	int res = 0;
	for(int i=0;i<26;i++)if(a & (1 << i)){
		for(int j=0;j<26;j++)if(b & (1 << j)){
			res |= can[i][j]; 
		}
	}
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		string s;
		cin>>s;
		can[s[1] - 'A'][s[2] - 'A'] |= 1 << (s[0] - 'A');
	}
	int k;
	cin>>k;
	while(k--){
		string t;
		cin>>t;
		int m = t.size();
		for(int i=1;i<=m;i++){
			for(int j=1;j<=m;j++){
				g[i][j] = 2.1e9;
				f[i][j] = 0;
			}
		}
		t = " " + t;
		for(int i=1;i<=m;i++){
			f[i][i] = 1 << (t[i] - 'A');
		}
		for(int len = 2;len <= m;len++){
			for(int l=1;l + len - 1<=m;l++){
				int r = l + len - 1;
				for(int k=l;k<r;k++){
					f[l][r] |= calc(f[l][k],f[k + 1][r]);
				}
				if(f[l][r] & (1 << ('S' - 'A'))){
					g[l][r] = 1;
				}else{
					for(int k=l;k<r;k++){
						g[l][r] = min(1ll * g[l][r],1ll * g[l][k] + g[k + 1][r]);
					}
				}
			}
		}
		if(g[1][m] > 2e9){
			cout<<"NIE\n";
		}else{
			cout<<g[1][m]<<'\n';
		}
	}
	return 0;
}
posted @ 2025-10-16 21:28  yutar  阅读(4)  评论(0)    收藏  举报