• 切割多边形


    Description

    在一个凸n边形中,通过不相交于n边形内部的对角线,把n边形拆分成若干三角形。现在的任务是从键盘输入凸多边形的边数n,求不同剖分的方案数Cn。

    Analysis

    困难的一题。

    不谈1000的数据量加上大整数会不会TLE,先分析一下转移方程。

    不妨以点为突破口。显而易见,每个点要么被连接,要么不被连接。

    • 当不被连接时,其相邻两点一定连接,则n边形转化为n-1边形。
    • 当被连接时,可以与n-3个点相连,那么相连之后分割出i边形和n-i+2边形。为了防止重复,即连接第i个点时要保证不连接i+1n-3,可以将n-i+2边形转化为n-i+1边形,即自动视为此点不与i+1n-3个点相连。

    动规方程:

    dp[i]=dp[i-1]+dp[3]*dp[i-2]+...+dp[i-1]*dp[2]
    

    因为dp[2]=1,所以又可以写成

    dp[i]=dp[2]*dp[i-1]+...+dp[i-1]*dp[2]
    

    状态转移复杂度为O(n^2),再算上高精度处理O(1000),TLE。

    那么,怎么通过O(n)乃至更小的复杂度转移状态呢。我不知道,所以看了hj解题报告,了解了Catlan Queue。

    不进行证明。

    Code

    #include <bits/stdc++.h>
    
    struct bigint{
    	int len,num[5010];
    	bigint operator = (int eq){
    		len=0;
    		memset(num,0,sizeof(num));
    		while(eq){
    			num[len++]=eq%10;
    			eq/=10;
    		}
    		return *this;
    	}
    	bigint operator * (bigint mt){
    		bigint ans;
    		ans.len=len+mt.len-1;
    		memset(ans.num,0,sizeof(ans.num));
    		for(int i=0;i<len;i++)
    			for(int j=0;j<mt.len;j++)
    				ans.num[i+j]+=num[i]*mt.num[j];
    		int add=0;
    		for(int i=0;i<ans.len;i++){
    			ans.num[i]+=add;
    			add=ans.num[i]/10;
    			ans.num[i]%=10;
    		}
    		while(add){
    			ans.num[ans.len++]=add%10;
    			add/=10;
    		}
    		return ans;
    	}
    	bigint operator * (int mt){
    		bigint bigmt;
    		bigmt=mt;
    		return *this*bigmt;
    	}
    	bigint operator *= (int mt){
    		return *this=*this*mt;
    	}
    	bigint operator / (int dv){
    		bigint ans;
    		ans.len=len;
    		memset(ans.num,0,sizeof(ans.num));
    		int bc=0;
    		for(int i=len-1;i>=0;i--){
    			bc=bc*10+num[i];
    			if(bc>=dv)ans.num[i]=bc/dv,bc%=dv;
    		}
    		while(ans.len-1&&!ans.num[ans.len-1])ans.len--;
    		return ans;
    	}
    	bigint operator /= (int dv){
    		return *this=*this/dv;
    	}
    	friend std::ostream& operator << (std::ostream& out,bigint ans){
    		for(int i=ans.len-1;i+1;i--)
    			out<<ans.num[i];
    		return out;
    	}
    };
    
    int n;
    
    bigint Catlan(int x){
    	bigint ans;
    	ans=x+2;
    	for(int i=x+3;i<=x*2;i++)
    		ans*=i;
    	for(int i=2;i<=x;i++)
    		ans/=i;
    	return ans;
    }
    
    int main(){
    	freopen("test.in","r",stdin);
    	freopen("test.out","w",stdout);
    	std::cin>>n;
    	if(n==1)std::cout<<0<<std::endl;
    	else if(n<4)std::cout<<1<<std::endl;
    	else std::cout<<Catlan(n-2)<<std::endl;
    	return 0;
    }
    
  • 相关阅读:
    推销
    5132. 颜色交替的最短路径
    5130. 等价多米诺骨牌对的数量
    @babel/plugin-transform-runtime和@babel/preset-env的区别
    5128. 最深叶节点的最近公共祖先(二叉树)
    1094. 拼车
    1109. 航班预订统计(数组)
    5129. 表现良好的最长时间段(数组)
    path.resove 和 path.join
    【原生】 call、apply、bind 的基本使用方法,已经解析了某些源码
  • 原文地址:https://www.cnblogs.com/qswx/p/9492564.html
Copyright © 2020-2023  润新知