zoukankan      html  css  js  c++  java
  • 可可西里

    Description

    如果假设一个藏羚羊群体有N只羊,就可以把它们的领地当做一个N*N的方阵,在这个方阵上第I列的第I行都有一个圣地,它们不会居住在圣地,同时每行每列只能居住一只羚羊。于是他们很快算出一个有N只羊的藏羚羊群体的居住地分布方法数。

    Analysis

    如果不考虑圣地的因素干扰,这题很容易写出动规方程:

    dp[i]=dp[i-1]*i
    

    什么意思呢?就是对于n·n的方阵由(n-1)·(n-1)转移而来,原先的行和列都已经占满,所以第n只绵羊只能放在新增的列和行上,新增的列当然是第n列,而新增的行可以放在n·n方阵的任意一行,共n种情况。

    然而 圣地的存在限制了绵羊的放置,对于n·n方阵,显然的我们只需要考虑第n列绵羊的防治,它只能放在1~n-1行,放置之后,将原方阵切割,再合并之后可以看做(n-1)·(n-1)矩形。

    4·4原图:

    放在第四列第一行后:

    舍去无法放置的区域后:

    放在第四列第二行后可以画出:

    等效替换后,发现他们都可以表示为(n-1)·(n-1)方阵下最后一行无圣地的情况,令无圣地情况为dp[n-1][1],那么dp[n][0]=dp[n-1][1]·(n-1)。

    而此特殊情况如何计算呢?显然对于前n-1行处理方式与普通情况是一样的,只是最后一行又多了dp[i-1][0]种情况。

    dp[i][0]=dp[i-1][1]*(n-1)
    
    dp[i][1]=dp[i-1][0]+dp[i][0]
    

    因为数据太大,需要使用高精度。

    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 eq){
    		len=eq.len;
    		memset(num,0,sizeof(num));
    		for(int i=0;i<len;i++)
    			num[i]=eq.num[i];
    		return *this;
    	}
    	bigint operator + (bigint ad){
    		bigint ans;
    		ans.len=std::max(len,ad.len);
    		memset(ans.num,0,sizeof(ans.num));
    		int add=0;
    		for(int i=0;i<ans.len;i++){
    			int res=num[i]+ad.num[i]+add;
    			ans.num[i]=res%10;
    			add=res/10;
    		}
    		if(add)ans.num[ans.len++]=add;
    		return ans;
    	}
    	bigint operator * (int mt){
    		bigint ans;
    		ans.len=len;
    		memset(ans.num,0,sizeof(ans.num));
    		for(int i=0;i<len;i++)
    			ans.num[i]=num[i]*mt;
    		int add=0;
    		for(int i=0;i<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;
    	}
    	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 dp[1010][2];
    
    int main(){
    	freopen("keke.in","r",stdin);
    	freopen("keke.out","w",stdout);
    	std::cin>>n;
    	dp[1][0]=0;
    	dp[1][1]=1;
    	for(int i=2;i<=n;i++)
    			dp[i][0]=dp[i-1][1]*(i-1),dp[i][1]=dp[i][0]+dp[i-1][0];
    	std::cout<<dp[n][0]<<std::endl;
    	return 0;
    }
    
  • 相关阅读:
    JAVA线程池
    数据库三范式和反三范式
    nvm切换node的版本
    vue源码解析一
    css实现气泡样式
    openlayer 地图实现圈选框选清楚 选择地图区域
    vs code开发常用插件
    记录几个好用的工具
    03 使用css改变页面样式
    02 常用的html元素
  • 原文地址:https://www.cnblogs.com/qswx/p/9492772.html
Copyright © 2011-2022 走看看