NAIPC2016 F (计数dp)

 dp好弱鸭。。

这个题目关键在于递推方程,这个递推方程怎么得到的呢?

dp[i][j]代表第行,放j个绸缎可以放的的方法数,三个for循环分别控制,第一个for控制在前i列放绸缎,j控制在这前i列放单位绸缎的个数,放绸缎的个数不超过长*宽,所以用当j+k>n break。k控制在一列中放绸缎的数量,放绸缎的数量不超k个。

再分析一下dp方程,dp[i][j+k]=dp[i][j+k]+dp[i-1][j],用一个样例分析,dp2,3就是在前2列放3个单位的绸缎有多少种方法,dp2,3=dp1,0+dp1,1+dp1,2+dp1,3为什么等于这四个数的和呢?其实分别就是在第一列放0个第二列放3个、第一列放1个第二列放2个,第一列放2个第二列放0个,第一列放0个第二列放3个的这四种方法数之和。

这个题目还有一点就是对于一个被减数的取模,因为被减数可能为负,所以把被减数加上mod后加成正的以后再取模。

#include<iostream>
#include<cstring>
#define mod 1000000007
typedef long long ll;
using namespace std;
ll dp[110][10010];
int main(){
	ll n,w,h;
	cin>>n>>w>>h;
	
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	for(int i=1;i<=w;i++){
		for(int j=0;j<=n;j++){
			for(int k=0;k<=h;k++){
				if(j+k>n)break;
				dp[i][j+k]=dp[i][j+k]+dp[i-1][j];
				if(dp[i][j+k]>mod) 
					dp[i][j+k]-=mod;
			}
		}
	}
//	for(ll i=1;i<=w;i++){
//		for(ll j=1;j<=n;j++)
//			cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;
//	}
	ll sum=0;
	for(int i=0;i<=n;i++){
		sum+=dp[w][i];
		if(sum>mod) 
			sum-=mod;
	}
//	sum-=n/w+1;
	if(n>w*h) 
		sum-=h+1;
    else 
		sum-=n/w+1;
	while(sum<=0){
		sum+=mod;
	}sum%=mod;
	cout<<sum<<endl;
}

猜你喜欢

转载自blog.csdn.net/samscream/article/details/82953509