1. 程式人生 > 實用技巧 >【CQOI2008】矩陣的個數

【CQOI2008】矩陣的個數

反正對今天是無言。。。

Description

給出一個N行3列非負整數矩陣的各行各列之和,統計有多少個矩陣滿足此條件。輸出答案模10^17的值。

Input

第一行包含四個正整數N,c1, c2, c3,即行數與三列之和。第二行包含N個正整數,即各行三個數之和。每行每列之和均不超過125。

Output

僅一個數,滿足條件的矩陣個數模10^17的值。

Sample Input

3 2 3 4
1 2 6

Sample Output

17

Data Constraint

Hint

1<=N<=200

其實這道題就是DP,如果用暴搜,必爆。。。

它的思路是這樣滴

我們可以發現,只要決定了第一列和第二列的數,第三列的數就已經是確定了

那麼我們就只需要考慮第一二列的數就行

那麼我們需要五層迴圈:1.i【1~n】,用來表示的是到第幾行

           2.j【0~c1】,用來表示第一列的總和

           3.k【0~c2】,用來表示第二列的總和

           4.x【0~min(j,a[i])】,用來表示第一列的取值,但是取值不能超過它的限制條件

           5.y【0~min(k,a[i]-x)],用來表示第二列的取值,但是取值不能超過它的限制條件

mlg大佬的幫助下,我成功地意識到滾動的重要性,主要是空間只給了60MB左右,易燃易爆炸?

其實我們可以發現,我們每一層的狀態都是一層層推下來,那麼的話,我們就可以發現,我們到達的這一層的狀態只與上一層狀態有關

所以一波滾動走起

對了忘記說動態方程了,其實到這裡很明顯,就是:

f[i&1][j][k]+=f[(i&1)^1][j-x][k-y],f[i&1][j][k]%=mod 

程式碼獻上:     

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
 ll n,c1,c2,c3;
 ll a[300];
 ll f[2][150][150];
int main(){
	scanf("%lld%lld%lld%lld",&n,&c1,&c2,&c3);
	ll sum=0;
	for(long i=1;i<=n;i++){
	scanf("%lld",&a[i]);
	sum+=a[i];}
	if(sum!=c1+c2+c3) {
		printf("0");
		return 0;
	}        //特判一下,看看所給資料是否滿足條件
	f[0][0][0]=1; //初始化,注意
	for(ll i=1;i<=n;i++)
	for(ll j=0;j<=c1;j++)
	for(ll k=0;k<=c2;k++){
	f[i&1][j][k]=0; //記住這一個!!要在它
	for(ll x=0;x<=min(j,a[i]);x++)
	for(ll y=0;y<=min(k,a[i]-x);y++)
	f[i&1][j][k]+=f[(i&1)^1][j-x][k-y],f[i&1][j][k]%=100000000000000000;}	
	printf("%lld",f[n&1][c1][c2]);
}