Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 6467 | Accepted: 2397 |
Description
Being a bit mathematical, Bessie started wondering. Bessie noted that the hive has T (1 <= T <= 1,000) families of ants which she labeled 1..T (A ants altogether). Each family had some number Ni (1 <= Ni <= 100) of ants.
How many groups of sizes S, S+1, ..., B (1 <= S <= B <= A) can be formed?
While observing one group, the set of three ant families was seen as {1, 1, 2, 2, 3}, though rarely in that order. The possible sets of marching ants were:
3 sets with 1 ant: {1} {2} {3}
5 sets with 2 ants: {1,1} {1,2} {1,3} {2,2} {2,3}
5 sets with 3 ants: {1,1,2} {1,1,3} {1,2,2} {1,2,3} {2,2,3}
3 sets with 4 ants: {1,2,2,3} {1,1,2,2} {1,1,2,3}
1 set with 5 ants: {1,1,2,2,3}
Your job is to count the number of possible sets of ants given the data above.
Input
* Lines 2..A+1: Each line contains a single integer that is an ant type present in the hive
Output
Sample Input
3 5 2 3 1 2 2 1 3
Sample Output
10
Hint
Three types of ants (1..3); 5 ants altogether. How many sets of size 2 or size 3 can be made?
OUTPUT DETAILS:
5 sets of ants with two members; 5 more sets of ants with three members
dp[i][j]表示从i个家族中取j只蚂蚁,m=min(j,a[i]);可以从第i个家族中取k个(0<=k<=m)
则从前i-1个家族中取j-k个因此dp[i][j]=∑dp[i-1][j-k](0<=k<=m);
按照此种方法求解时间复杂度O(100*T*A);
优化:
(1).当j<=a[i],即j-1<a[i]时:m=j;此时
dp[i][j]=dp[i-1][0]+dp[i-1][1]+……+dp[i-1][j-1]+dp[i-1][j];
而dp[i-1][0]+dp[i-1][1]+….+dp[i-1][j-1]=dp[i][j-1];因此得出
dp[i][j]=dp[i][j-1]+dp[i-1][j];
(2).当j>a[i]时,m=a[i];此时
dp[i][j]=dp[i-1][j-a[i]]+dp[i-1][j-a[i]+1]+….+dp[i-1][j];
类似以上方法,可得
dp[i-1][j-a[i]-1]+dp[i-1][j-a[i]]+…+dp[i-1][j-1]=dp[i][j-1];由此得出
dp[i][j]=dp[i][j-1]-dp[i-1][j-a[i]-1]+dp[i-1][j]。
这样转化以后,我们在求每一个状态时可以直接由之前状态得到,
这样时间复杂度就降到了O(T*A)。
O(100*T*A)代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
int a[1002],mod=1e6;
int dp[2][100002];//滚动数组节省内存
using namespace std;
int main()
{
int T,A,S,B;
while(scanf("%d%d%d%d",&T,&A,&S,&B)!=EOF)
{
memset(a,0,sizeof(a));
for(int i=1;i<=A;i++)
{
int t;scanf("%d",&t);
a[t]++;
}
memset(dp,0,sizeof(dp));
dp[0][0]=1,dp[1][0]=1;//从每个家族取零个都有一种方法。
for(int i=1;i<=T;i++)
{
for(int j=1;j<=B;j++)//B之后的状态不必考虑
{
int m=min(j,a[i]);
dp[i%2][j]=0;//由于开的是滚动数组,dp[i%2][j]在之前可能已经被赋值。
for(int k=0;k<=m;k++)
{
dp[i%2][j]=(dp[i%2][j]+dp[(i-1)%2][j-k])%mod;
}
}
}
int s=0;
for(int i=S;i<=B;i++)
s=(s+dp[T%2][i])%mod;
printf("%d\n",s);
}
return 0;
}
O(T*A)代码:
#include<iostream>
#include<string.h>
#include<algorithm>
#include<cstdio>
int a[1003], dp[2][100002],mod=1e6;
using namespace std;
int main()
{
int T, A, S, B;
while (scanf("%d%d%d%d", &T, &A, &S, &B) != EOF)
{
memset(a, 0, sizeof(a));
for (int i = 1; i <= A; i++)
{
int t; scanf("%d", &t);
a[t]++;
}
memset(dp, 0, sizeof(dp));
dp[0][0] = 1; dp[1][0] = 1;
int sum = 0;
for (int i = 1; i <= T; i++)
{
for (int j = 1; j <=B; j++)
{
if (j <= a[i])
dp[i&1][j] = (dp[i&1][j - 1] + dp[(i - 1)&1][j])%mod;
else //为了避免相减出现负数的情况,每次加mod
dp[i&1][j] = (dp[i&1][j - 1] + dp[(i - 1)&1][j] - dp[(i-1)&1][j - 1 - a[i]]+mod)%mod;
}
}
for(int i=S;i<=B;i++)
sum=(sum+dp[T&1][i])%mod;
printf("%d\n", sum%mod);
}
return 0;
}