題目鏈接如下:
http://community.topcoder.com/stat?c=problem_statement&pm=11496&rd=14545
這個題的狀態很好寫出,假設dp[i][j]表示:到第i個人其得到j分時的情況個數,那麼可以寫出狀態:
dp[i][j]=sum(dp[i-1][p],p>j)*S[i][j],其中S[i][j]表示第i個人得到j分的方法數。不難看出sum(dp[i-1][p],p>j)可以在計算的過程中預處理得出,如果S[i][j]也可以預處理的話,那麼狀態轉移的時間複雜度可以優化到O(nm),n爲人數,m爲最大分值,可行!
現在我們來處理S[][],對於只做出一題和兩題的情況,其方法數很好算,對於做出三題的情況,我是先計算將j得分分配給後面兩題有多少中情況,然後加上最前面一題,其實方法有些挫的,還有更好的方法,我做的時候沒有想到。一開始想的時候,很容易聯想到將n個相同的球分給3個不同盒子的問題,初中問題啊,用插板法(一開始還忘記了。。。),其實解這個題的思路並不是插板法,但是就當溫習啦。
PS:一開始實現的時候是用二維數組的,然後Test的時候就會有Kill信號,最後都改爲一維數組了。另外,發現自己的代碼能力越來越差,寫的時候很多小Bug,然後有cha不出來。
代碼如下:
#define MOD (1000000007)
#define MAXN 200000
#define _min(a,b) ((a)<(b)?(a):(b))
#define _max(a,b) ((a)>(b)?(a):(b))
int pnum[22];
int toppoint[22];
long long S[MAXN+5];
long long dp[22][MAXN+5];
long long pre[MAXN+5];
long long Stemp[MAXN];
long long psum[MAXN];
void calcS2(int ind,vector<int>& points,int a,int b){
int i;
for(i=2;i<=toppoint[ind];i++){
int P=i;
P--;
if(P>points[a]){
S[i]=_min(points[a],points[b]-P+points[a]);
}
else{
S[i]=P;
}
}
}
void calcS3(int ind,vector<int>& points){
int i,P;
for(i=3;i<=toppoint[ind];i++){
int l=_max(2,i-points[0]);
int r=_min(i-1,points[1]+points[2]);
S[i]=(Stemp[r]-Stemp[l-1]+MOD)%MOD;
}
}
void calcS(vector<int>& points,vector<string>& des,int i){
int j;
memset(S,0,sizeof(S));
if(pnum[i]==0) S[0]=1;
else if(pnum[i]==1){
for(j=1;j<=toppoint[i];j++)
S[j]=1;
}
else if(pnum[i]==2){
if(des[i][0]=='Y'&&des[i][1]=='Y'){
calcS2(i,points,0,1);
}
else if(des[i][0]=='Y'&&des[i][2]=='Y'){
calcS2(i,points,0,2);
}
else{
calcS2(i,points,1,2);
}
}
else{
calcS3(i,points);
}
}
class SRMIntermissionPhase
{
public:
int countWays(vector <int> points, vector <string> description)
{
int n=description.size();
int i,j,P;
int sum=0;
for(i=0;i<n;i++){
pnum[i]=0;
sum=0;
for(j=0;j<3;j++)
if(description[i][j]=='Y'){pnum[i]++;sum+=points[j];}
toppoint[i]=sum;
}
memset(S,0,sizeof(S));
memset(Stemp,0,sizeof(Stemp));
//calc S[][]
for(i=2;i<=points[1]+points[2];i++){
P=i;
P--;
if(P>points[1]){
Stemp[i]=_min(points[1],points[2]-P+points[1]);
}
else{
Stemp[i]=P;
}
}
for(i=3;i<=points[1]+points[2];i++)
Stemp[i]=(Stemp[i-1]+Stemp[i])%MOD;
//calc dp[][]
memset(dp,0,sizeof(dp));
memset(pre,0,sizeof(pre));
calcS(points,description,0);
for(j=toppoint[0];j>=pnum[0];j--){
dp[0][j]=S[j];
}
for(i=1;i<n;i++){
calcS(points,description,i);
pre[MAXN+1]=0;
for(j=MAXN;j>=0;j--)
pre[j]=(pre[j+1]+dp[i-1][j])%MOD;
for(j=toppoint[i];j>=pnum[i];j--){
dp[i][j]=(pre[j+1]*S[j])%MOD;
}
}
long long res=0;
for(j=toppoint[n-1];j>=pnum[n-1];j--)
res=(res+dp[n-1][j])%MOD;
return (int)res;
}
};