dp--递推

 

Attack on Titans

 ZOJ - 3747 

题意:有三种兵A,B,C,选n个排队,问(至少m个连续的B和至多k个连续的C)的方案数。

理解了很长时间,,,好菜=_=||

先贴上代码,有空来写

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn = 1000010;
 5 const int mod = 1000000007;
 6 ll d[maxn][4];
 7 ll n,m,k;
 8 ll cal(int u){
 9     d[0][0]=1;
10     d[0][1]=0;
11     d[0][2]=0;
12     for(int i=1;i<=n;i++){
13         ll sum=(d[i-1][0]+d[i-1][1]+d[i-1][2])%mod;
14         d[i][2]=sum;
15 
16         if(i<=u) d[i][0]=sum;
17         else if(i==u+1) d[i][0]=(sum-1)%mod;
18         else d[i][0]=(sum-d[i-u-1][1]-d[i-u-1][2])%mod;
19 
20         if(i<=k) d[i][1]=sum;
21         else if(i==k+1) d[i][1]=(sum-1)%mod;
22         else d[i][1]=(sum-d[i-1-k][0]-d[i-1-k][2])%mod;
23     }
24     return (d[n][0]+d[n][1]+d[n][2])%mod;
25 }
26 int main(){
27     while(scanf("%d%d%d",&n,&m,&k)!=EOF){
28         ll ans=cal(n);
29         ans=((ans-cal(m-1))%mod+mod)%mod;
30         printf("%lld
",ans);
31     }
32     return 0;
33 }
View Code

Mex

 HDU - 4747

题意:求所有区间的mex和。mex值为没有在该区间出现过的最小非负整数。

感谢大神的分析http://blog.csdn.net/cc_again/article/details/11856847,可是对我等蒟蒻还是难以理解~~

又想了很久才有点明白orz▄█▀█●,模拟一波~

我们求以i结尾的区间的mex和f[i],再累加就是答案。为什么要这样求呢?因为可以从f[i-1]推出f[i]。记f[i] = f[i-1] +temp

初始temp=0,下面求temp

现在看求第f[i]的过程,现在数列中加入了新的数val=a[i]

首先可以想到的是,对于上一个出现val的位置p(即代码里的last[a[i]]),p之前的元素(即f[p-1],f[p-2],…… f[1])对f[i]的结果没有任何影响(因为在a[i]之前的区间里已经有val了)

现在考虑p到i之间的元素,可以知道,他们中可能有  因为没有val这个元素  而导致 到f[i-1]的mex是比小于等于val的,而现在加入了val,那么它的mex就会增大

那么加入val之后都对哪些元素有影响呢?

这里定义一个覆盖:如果j到i之间出现了0到x的所有值,那么称x的 覆盖为j。

对于新加入的val,求出val-1的覆盖j,那么对于p(上一个val的位置)+1到  min ( j , last[val] )之间的元素,他们到a[i]=val这个位置的mex都【至少】是val+1,本来他们到a[i-1]的mex是val,即都变大了1。此时temp += min ( j , last[val] )  - p。

上面为什么说至少呢?说明还有可能大于val+1。

举个例子,6  4  0  2 1 5  3  。现在处理第七位3, f[7] = f[6] + temp  = 9 + temp 。

加入3之后,根据上面的步骤,我们先求得2的覆盖是3,所以1到3的mex至少变成了4(他们在加入3之前是3),但是显然6的mex是7,4的mex是6,这一步只使得a[3]的mex值正确了。 此时temp +=3。 temp=3。

这是因为前面已经出现了4,5,6,在加入3之前他们没有任何,加入3导致他们也有了作用。这就等价于我们又加入了4,5,6

所以我们还要求3的覆盖,4的覆盖,5的覆盖……,直到mex值完全正确。

步骤如下:

加入4,last[4]=2,3的覆盖是3,所以1到2 ( min(3,last[4] ) 的mex变成了5,  此时temp +=2。 temp=5。

继续,加入5,last[5]=6,4的覆盖是2,所以1到2 ( min(2,last[5] ) 的mex变成了6, 此时temp +=2。 temp=7。

继续,加入6,last[6]=1,5的覆盖是1,所以1到1 ( min(2,last[5] ) 的mex变成了7, 此时temp +=1。 temp=8。

到此就结束了。

所以f[i] = f[i-1] +temp = 9 + 8 =17 。

下面代码里的mp就是上面分析中说到的覆盖。

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 int a[200002],last[200002],mp[200002];  
 5 int main()
 6 {
 7     int n,cnt;
 8     while(scanf("%d",&n)&&n){
 9             for(int i=1;i<=n;i++)
10                   scanf("%d",&a[i]);
11             
12             ll ans=0;
13             ll temp=0,pre;
14             memset(last,0,sizeof(last));
15             memset(mp,0,sizeof(mp));
16             for(int i=1;i<=n;i++){
17                   if(a[i]<=n){   //如果大于n,不会影响任何元素,mex的和不变
18                         pre=last[a[i]];
19                         last[a[i]]=i;
20                         for(int j=a[i];j<=n;j++){
21                               if(j) mp[j]=min(mp[j-1],last[j]);
22                               else mp[j]=last[j];
23                               if(mp[j]>pre){
24                                     temp+=mp[j]-pre;
25                               }else break;
26                         }
27                   }
28                   ans+=temp;
29             }
30             printf("%I64d
",ans);
31     }
32     return 0;
33 }
View Code

The King’s Ups and Downs

 HDU - 4489 

题意:给n个人(身高为1到n)排队,问高矮相间的排队方式有几种。

题解:链接

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=22;
 5 ll c[maxn][maxn];
 6 ll dp[maxn][maxn];
 7 ll ans[maxn];
 8 int n;
 9  void init(){
10      c[1][0]=c[1][1]=1;
11      for(int i=2;i<maxn;i++){
12         c[i][0]=1;
13         for(int j=1;j<=i;j++)
14             c[i][j]=c[i-1][j-1]+c[i-1][j];
15      }
16  }
17 int main(){
18     init();
19     dp[0][0]=dp[0][1]=1;
20     dp[1][0]=dp[1][1]=1;
21     ans[1]=1;
22     for(int i=2;i<=20;i++){
23         ans[i]=0;
24         for(int j=1;j<=i;j++){
25             ans[i]+=(dp[j-1][0]*dp[i-j][1]*c[i-1][j-1]);
26         }
27         dp[i][0]=dp[i][1]=ans[i]>>1;
28     }
29     int t;
30     scanf("%d",&t);
31     while(t--){
32         int i,k;
33         scanf("%d%d",&i,&k);
34         printf("%d %lld
",i,ans[k]);
35     }
36 }
View Code

Number String

 HDU - 4055

题意:让你求满足字符串s的n排列有多少种。要求,如果s[i]为D,那么第i位小于第i-1位,如果s[i]为I,那么第i位大于第i-1位,s[i]为?时无所谓。

用dp[i][j]表示第i为排小于等于j的方案数。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int mod=1000000007;
 4 const int maxn=1010;
 5 char s[maxn];
 6 int dp[maxn][maxn];
 7 
 8 int main(){
 9     while(scanf("%s",s+2)!=EOF){
10         memset(dp,0,sizeof(dp));
11         int n=strlen(s+2)+1;
12         dp[1][1]=1;
13         for(int i=2;i<=n;i++){
14             for(int j=1;j<=i;j++){
15                 dp[i][j]=dp[i][j-1];
16                 if(s[i]=='D'||s[i]=='?') dp[i][j]=(dp[i][j]+dp[i-1][j-1])%mod;
17                 if(s[i]=='I'||s[i]=='?') dp[i][j]=((dp[i][j]+(dp[i-1][i-1]-dp[i-1][j-1]))%mod+mod)%mod;
18             }
19         }
20         printf("%d
",dp[n][n]);
21     }
22     return 0;
23 }
View Code

Water Treatment Plants

 POJ - 1205 

原文地址:https://www.cnblogs.com/yijiull/p/7307955.html