• CCF CSP 201409-5 拼图


    在写这题时,先后根据不同博客学习了状压dp、矩阵快速幂,最后代码有些小bug参考了一位思路差不多的大佬以做出修改,在这里先感谢三位大佬,之后会在思路里贴出他们的博文链接;
    知乎上好多人喷CSDN怎样怎样,我觉得静下心来,在这一篇篇博文里还是能学到很多东西的嘛…

    思路:

    1.使用状态压缩动态规划来求递推式;
    2.不会状压dp的请看这位大佬的博客: https://blog.csdn.net/u011077606/article/details/43487421.大家可以认真学习博主在最前面提到的位运算,因为用一个int各位上的0/1来压缩状态的表示就是状压dp的特色。这位博主例1的问题与http://poj.org/problem?id=2411几乎一样,大家学习后可以用这道简单的例题来测试自己有没有掌握状压dp。
    3.这题和上面那位博主的例一有点像,只是将长方体换成了形状不一样的积木;另外最重要的就是n的范围达到了1015,如果用纯状压dp求解只能70分;怎样加速求解呢?这里我们可以采用矩阵快速幂;
    4.在学习矩阵快速幂之前,应该先学习快速幂的思想;然后再去学矩阵快速幂,这里是传送门:https://blog.csdn.net/wust_zzwh/article/details/52058209.在仔细学习后,我们会明白,只要求一个转移矩阵,再将其求n次方,最后乘以初始行列式即可。在求n次方的时候,就可以用快速幂思想了!
    5.最后贴一下另一位大佬的链接,正是靠着他的代码,我才得以逐步挑出自己代码中的小bug:https://blog.csdn.net/vcvycy/article/details/78243162.

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define rp(i,n) for(int i=0;i<n;i++)
    typedef long long ll;
    const int MOD=1000000007;
    const int M=(1<<7)+5;
    ll n;
    int m;
    int mm; //存放2^m 
    #define VACANT(i,s) ((i>=0)&&(i<m)&&(!((1<<i)&s)))//状态为S的列第i行是否为空 
    int t[M][M];//转移矩阵   i,j各代表状态为j时,下一列状态为i的个数 
    void dfs(int i,int state,int nex){//处理当前第i行的情况 
    	if(i==m){
    		t[nex][state]++;
    		return; 
    	}
    	if(!VACANT(i,state)) dfs(i+1,state,nex); //此格子被占用 		
    	if(VACANT(i,state)&&VACANT(i,nex)&&VACANT(i-1,nex)){//符合插入第一种积木的条件 	
    		dfs(i+1,state,nex|(1<<i)|(1<<(i-1)));
    	}
    	if(VACANT(i,state)&&VACANT(i,nex)&&VACANT(i+1,nex)){//符合插入第二种积木的条件 	
    		dfs(i+1,state,nex|(1<<i)|(1<<(i+1)));
    	}
    	if(VACANT(i,state)&&VACANT(i+1,state)&&VACANT(i+1,nex)){//符合插入第三种积木的条件 	
    		dfs(i+2,state,nex|(1<<(i+1)));
    	}
    	if(VACANT(i,state)&&VACANT(i+1,state)&&VACANT(i,nex)){//符合插入第四种积木的条件 	
    		dfs(i+2,state,nex|(1<<i));
    	}
    }
    int temp[M][M];
    void multi(int a[][M],int b[][M]){//计算矩阵(a*b)%MOD,结果存放在a中 
    	memset(temp,0,sizeof(temp));//每次初始化,因为下面是+=运算符 
    	rp(i,mm) rp(j,mm) rp(k,mm){
    		temp[i][j]=(temp[i][j]+1ll*a[i][k]*b[k][j]%MOD)%MOD;
    	}
    	rp(i,mm) rp(j,mm) a[i][j]=temp[i][j];
    }
    int res[M][M]; 
    void Pow(int a[][M],ll n){//快速幂求(a^n),结果存放在矩阵res中 
    	rp(i,M) res[i][i]=1;//初始化res=1;
    	while(n){
    		if(n&1) multi(res,a);//n为奇数
    		multi(a,a);//a=a^2;
    		n>>=1; //n/=2;
    	} 
    }
    int main(){
    	cin>>n>>m;
    	mm=(1<<m);
    	for(int i=0;i<=mm;i++) dfs(0,i,0);//搜索状态为i的列的下一列可以是什么状态 
    	Pow(t,n);	//转移矩阵自乘n次; 
    	cout<<res[0][0]; //初始行列式为[1;0;0;...;0];
    	return 0;
    }
    
  • 相关阅读:
    winform中的 listview的使用的讲解。
    快乐的一天从AC开始 | 20210716 | P1345
    快乐的一天从AC开始 | 20210715 | P4643
    快乐的一天从AC开始 | 20210714 | P3594
    快乐的一天从AC开始 | 20210713 | P3557
    快乐的一天从AC开始 | 20210712 | P2251
    P7294-[USACO21JAN]Minimum Cost Paths P【单调栈】
    AT4353-[ARC101D]Robots and Exits【LIS】
    2021牛客暑期多校训练营9C-Cells【LGV引理,范德蒙德行列式】
    Loj#2880-「JOISC 2014 Day3」稻草人【CDQ分治,单调栈,二分】
  • 原文地址:https://www.cnblogs.com/yuhan-blog/p/12308924.html
Copyright © 2020-2023  润新知