week15作业
C-ZJM与纸条
题目大意
输入n组字符串,求串1在串2中的出现次数。
题解——poj 3461 Oulipo
CSP-M4
C-宇宙狗的危机
题目大意
给出一个升序序列,问该序列能否构成一棵二叉搜索树且任意树边相连的两个节点的gcd都超过1
题解
区间DP
先考虑最简单的DP,
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示区间
[
i
,
j
]
[i,j]
[i,j]能否构成以
k
k
k为根的二叉搜索树,根据二叉搜索树的特性左子树小与根节点,右子树大于根节点在加上序列是升序的,所以我们只要确定了
k
k
k,那么区间
[
i
,
k
−
1
]
[i,k-1]
[i,k−1]构成左子树,区间
[
k
+
1
,
j
]
[k+1,j]
[k+1,j]构成右子树,那么
f
[
i
]
[
j
]
[
k
]
=
t
r
u
e
f[i][j][k]=true
f[i][j][k]=true的条件其实就是存在x,y使得
f
[
i
]
[
k
−
1
]
[
x
]
=
t
r
u
e
且
g
c
d
(
a
[
x
]
,
a
[
k
]
)
>
1
,
f
[
k
+
1
]
[
j
]
[
y
]
=
t
r
u
e
且
g
c
d
(
a
[
y
]
,
a
[
k
]
)
>
1
f[i][k-1][x]=true且gcd(a[x],a[k])>1,f[k+1][j][y]=true且gcd(a[y],a[k])>1
f[i][k−1][x]=true且gcd(a[x],a[k])>1,f[k+1][j][y]=true且gcd(a[y],a[k])>1,然后特别考虑子树为空的情况即可。(具体实现见下面代码注释掉的部分)
但是这样时间复杂度是
O
(
n
4
)
O(n^4)
O(n4),空间复杂度是
n
3
n^3
n3,都无法承受,所以我们需要考虑去优化区间DP。假设我们选定了一个区间
[
i
,
j
]
[i,j]
[i,j]让他去构成子树,那么这个子树的根节点只能是i-1,j+1,也就是说我们
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]中k这一维可以进行优化,
f
[
i
]
[
j
]
[
0
]
f[i][j][0]
f[i][j][0]表示区间
[
i
,
j
]
[i,j]
[i,j]构成的子树是否能以i-1为根,
f
[
i
]
[
j
]
[
1
]
f[i][j][1]
f[i][j][1]表示区间
[
i
,
j
]
[i,j]
[i,j]构成的子树是否能以j+1为根,然后进行DP转移即可。
另外因为DP的过程中需要gcd,可以在DP开始前预处理出gcd的关系,这样就不用每次调用gcd过程了,节省时间。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 703
using namespace std;
int a[N],pd[N][N],n,f[N][N][2];
int gcd(int a,int b) {return b == 0 ? a : gcd(b,a%b);}
int main()
{
int T; scanf("%d",&T);
while(T--) {
scanf("%d",&n);
//memset(dp,0,sizeof(dp));
memset(f,0,sizeof(f));
memset(pd,0,sizeof(pd));
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) pd[i][j]=gcd(a[i],a[j])>1?1:0;
if(n==1) {
printf("Yes\n");
continue;
}
/*for (int i=1;i<=n;i++) dp[i][i][i]=1;
for (int l=2;l<=n;l++)
for (int i=1;i+l-1<=n;i++){
int j=i+l-1;
for (int k=i;k<=j;k++) {
bool pdl=false; bool pdr=false;
if(k==i) pdl=true;
if(k==j) pdr=true;
for(int t=i;t<k;t++)
if(pd[t][k]&&dp[i][k-1][t]) pdl=true;
for(int t=k+1;t<=j;t++)
if(pd[t][k]&&dp[k+1][j][t]) pdr=true;
dp[i][j][k]=pdl&pdr;
}
}
bool mark=false;
for (int i=1;i<=n;i++) mark|=dp[1][n][i];
if(mark) printf("Yes\n");
else printf("No\n");*/
bool mark=false;
for (int i=1;i<=n;i++) f[i][i][0]=pd[i-1][i],f[i][i][1]=pd[i][i+1];
for (int l=2;l<=n;l++)
for (int i=1;i+l-1<=n;i++) {
int j=i+l-1;
for(int k=i;k<=j;k++) {
bool pdl=false; bool pdr=false;
if(k==i) pdl=true;
if(k==j) pdr=true;
pdl|=f[i][k-1][1];
pdr|=f[k+1][j][0];
if (!pdl||!pdr) continue;
if (i==1&&j==n) {
mark=true;
continue;
}
if(pd[k][i-1]) f[i][j][0]=true;
if(pd[k][j+1]) f[i][j][1]=true;
}
}
if(mark) printf("Yes\n");
else printf("No\n");
}
return 0;
}