week15作业+CSP-M4

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,k1]构成左子树,区间 [ 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][k1][x]=truegcd(a[x],a[k])>1,f[k+1][j][y]=truegcd(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;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值