题意:
给你n种染料,每种染料可以使用Ci次,同一种染料不能连续使用,求用完所有染料的方案数为多少种?
思路:
纯动态规划,我们用dp[a][b][c][d][e][last]描述一个还可以使用一次的染料为a种,还可以使用两次的染料为b种,.....,可以使用五次的染料为f种时上一次染色为使用还可以使用last次的染料。
我们可以分析状态是怎么来的得出状态转化方程,例如,dp[a][b][c][d][e][1]时,如果上上次不为2,则上一次则使用的为a+1种染料的其中一种,如果上上次为2,则说明上一次(a+1)种有一种不能使用,所以为a种中的其中一种。
代码:
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #define inf 1000000007 #define eps 0.00000001 using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn=100005; inline int read() { char c=getchar(); int f=1, x=0; while(c>'9'||c<'0') { if(c=='-') { f=-1; } c=getchar(); } while(c<='9'&&c>='0') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); } return f*x; } ll dp[20][20][20][20][20][10], a[10]; int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) { int c; scanf("%d",&c); a[c]++; } dp[a[1]][a[2]][a[3]][a[4]][a[5]][1]=1; for(int r=n;r>=0;r--) { for(int l=n;l>=0;l--) { for(int k=n;k>=0;k--) { for(int j=n;j>=0;j--) { for(int i=n;i>=0;i--) { ll p=0; for(int o=1;o<=5;o++) { if(o==2) { p=p+dp[i+1][j][k][l][r][o]*(i); p=p%inf; } else { p=p+dp[i+1][j][k][l][r][o]*(i+1); p=p%inf; } } dp[i][j][k][l][r][1]=max(dp[i][j][k][l][r][1],p); p=0; for(int o=1;o<=5;o++) { if(i!=0) { if(o==3) { p=p+dp[i-1][j+1][k][l][r][o]*(j); p=p%inf; } else { p=p+dp[i-1][j+1][k][l][r][o]*(j+1); p=p%inf; } } } dp[i][j][k][l][r][2]=max(dp[i][j][k][l][r][2],p); p=0; for(int o=1;o<=5;o++) { if(j!=0) { if(o==4) { p=p+dp[i][j-1][k+1][l][r][o]*(k); p=p%inf; } else { p=p+dp[i][j-1][k+1][l][r][o]*(k+1); p=p%inf; } } } dp[i][j][k][l][r][3]=max(dp[i][j][k][l][r][3],p); p=0; for(int o=1;o<=5;o++) { if(k!=0) { if(o==5) { p=p+dp[i][j][k-1][l+1][r][o]*l; p=p%inf; } else { p=p+dp[i][j][k-1][l+1][r][o]*(l+1); p=p%inf; } } } dp[i][j][k][l][r][4]=max(dp[i][j][k][l][r][4],p); p=0; for(int o=1;o<=5;o++) { if(l!=0) { p=p+dp[i][j][k][l-1][r+1][o]*(r+1); p=p%inf; } } dp[i][j][k][l][r][5]=max(dp[i][j][k][l][r][5],p); //printf("%d\n",dp[1][1][1][0][0][0]); } } } } } cout << dp[0][0][0][0][0][1] << endl; return 0; }