A、B、C题题解请移步http://www.cnblogs.com/Delostik/archive/2012/10/03/2710655.html
【D. Towers】
http://www.codeforces.com/contest/229/problem/D
题目大意:有n座塔,每次操作可以将第i座塔和第(i-1)座或第(i+1)座合并,新塔高度为两塔高度和。问最少多少次操作之后使得塔高度序列不降。
O(n^2)的方法:f[i]表示使前i座塔高度不降的最少操作次数,h[i]表示在执行f[i]操作的前提下,第i座塔(或者是由操作i合并成的新塔)的最小高度。
f[i]=min{f[j]},sum(j,i)>=h[j-1],0≤j≤i ; h[i]=sum(j,i)
预处理前缀和,枚举i,j,完毕。
里面用到一个贪心性质就是,离i最近的j肯定是最优的……因为如果有状态j'<j,f[j]=f[j'],那么h[j']>h[j]这是显然的……这就没必要了……
有人说单调队列……其实用单调队列实质上也是要枚举,没看出有什么实质性的不一样……
#include <iostream>
using namespace std;
int n,H,f[5010],sum[5010],h[5010];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>H;
sum[i]=sum[i-1]+H;
}
for(int i=1;i<=n;i++)
for(int j=i-1;j>=0;j--)
if(sum[i]-sum[j]>=h[j]){
f[i]=f[j]+i-j-1;
h[i]=sum[i]-sum[j];
break;
}
cout<<f[n]<<endl;
}
【E. Gifts】
http://www.codeforces.com/contest/229/problem/E
题目大意:有一些礼物,有些礼物有相同的名字单有不同的价格,有些礼物有相同的价格但可以有不同的名字。人喊一个礼物的名字,便会随机得到叫这个名字的其中一个礼物。问取得n个最大价值的礼物的概率是多少。
“名字相同的礼物有不同的价格”,这句话很重要。我们确定一个价值下限minv,那么在所有礼物里面有两类,一类是一定要选的(v[i]>minv),另一类是价值恰好等于minv,可能选其中的一部分。那句话就告诉我们,在每种名字的礼物中,最多有1个是可以选择“选”或者“不选”的。
于是我们把第二类挑出来单独搞,称它们为“可选礼物”。可知可选礼物个数choose=n-cnt个,cnt为价值大于minv的礼物数量。
f[i][j]表示前i种名字的礼物中,选取了j个可选的礼物。num[i]表示名字为i个礼物个数,must[i]表示名字为i的礼物中的必选礼物个数。
转移:1.若第i种名字的礼物中没有可选礼物,那么f[i][j]=f[i-1][j]/C(must[i],num[i])
2.若第i种名字的礼物中有可选礼物,那么令t=(n-cnt-j)/choose,表示在第i种礼物中选取一个可选礼物的概率。
f[i][j]=f[i-1][j]/C(must[i],num[i])*(1-t) 在i种中不选可选礼物
f[i][j+1]=f[i-1][j]/C(must[i],num[i]+1)*t 在i种中选可选礼物
#include <iostream>
#include <vector>
#include <iomanip>
#include <algorithm>
#define mn 1010
using namespace std;
vector<int> v[mn];
int n,m,x,minv,choose,cnt,tot,a[mn],must[mn],num[mn];
double C[mn][mn],f[mn][mn],tmp;
bool prob[mn];
int main(){
for(int i=0;i<=1000;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>num[i];
for(int j=1;j<=num[i];j++){
cin>>x;
v[i].push_back(x);
a[++tot]=x;
}
}
sort(a+1,a+1+tot);
minv=a[tot-n];
for(int i=1;i<=m;i++){
for(int j=0;j<v[i].size();j++)
if(v[i][j]>minv) cnt++,must[i]++;
else if(v[i][j]==minv) prob[i]=true;
choose+=prob[i];
}
f[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<=n-cnt;j++)
if(!prob[i]) f[i][j]=f[i-1][j]/C[num[i]][must[i]];
else{
tmp=(double)(n-cnt-j)/choose;
f[i][j]+=f[i-1][j]/C[num[i]][must[i]]*(1-tmp);
f[i][j+1]+=f[i-1][j]/C[num[i]][must[i]+1]*tmp;
}
choose-=prob[i];
}
cout<<fixed<<setprecision(10)<<f[m][n-cnt]<<endl;
}