数据范围:\(n<=3000,m<=300\),保证\(\forall i,\sum\limits_{j}p_{ij}=1000\)
ios
平常指望算不对。。c++
首先比较明显的一点是,每种类型是独立的,咱们能够分开来考虑算法
先来想一个比较直接的dpspa
用\(a[i][j]\)表示第\(i\)我的最喜欢\(j\)的几率,记\(f[c][i][j]\)表示前\(i\)我的里面刚好有\(j\)我的最喜好类型\(c\),转移显然:
\[ f[c][i][j]=f[c][i-1][j]*(1-a[i][j])+f[c][i-1][j-1]*a[i][j] \]
再考虑用\(g[i][j]\)表示第\(i\)种钞票携带了\(j\)张,指望拿走的数量,那么有转移:
\[ \begin{aligned} g[i][j]&=\sum\limits_{k=0}^mmin(k,j)*f[i][n][k]\\ &=\sum\limits_{k=0}^j k*f[i][n][k]+\sum\limits_{k=j+1}^mj*f[i][n][k]\\ \end{aligned} \]
而后咱们就能够用一个背包求出答案了,可是这样不管是空间仍是时间都很爆炸code
考虑\(\nabla g[i][j]=g[i][j]-g[i][j-1]\),也就是\(\nabla g[i][j]=\sum\limits_{k=j}^m f[i][n][k]\),首先注意到\(f[i][n][k]\)在\(i\)肯定的状况下大小必定是随着\(k\)的增大而增大的,而后再看回这个\(\nabla g[i][j]\),显然这个东西的值应该是非负的,而且当\(i\)必定,\(\nabla g[i][j]\)的值随着\(j\)的增大而单调不升blog
因此咱们能够获得一个结论,当\(i\)肯定的时候,\(g[i][j]\)是不降的,而且增幅逐渐降低ip
而后又由于每种钞票是独立的,咱们能够考虑一个贪心string
枚举\(n\)张钞票的每一张都选什么,对于每一种钞票\(c\),咱们维护一个隐性的\(p_c\)(说是“隐性”是由于在实现的时候你并不须要真的去维护这么一个东西),表示钞票\(c\)当前已经取了\(p_c\)张,那么从贪心的角度来看,咱们当前枚举到的这张钞票,确定但愿新加入这张钞票以后,对应的\(g[c]\)的增幅最大,因此就能够获得一个大体的流程:从\(1\)到\(n\)枚举每一张钞票选什么,对于第\(i\)张钞票,\(O(m)\)求得最大的\(g\)的增幅,加入答案,而后对于选择的这个种类\(c\),更新其\(g\)值it
如今的问题就是怎么维护增幅,为了方便下面只针对肯定的\(c\)类钞票进行表述io
一开始的时候\(p_c=0\),\(g[c][0]\)到\(g[c][1]\)的增幅是很好计算的,\(\nabla g[c][1]=1-\prod\limits_{i=1}^n(1-a[i][c])\),可是\(\nabla g[c][2]\)看起来就不能那么直接地进行计算了,因此咱们仍是看回这个式子:\(\nabla g[i][j]=\sum\limits_{k=j}^mf[i][n][k]=1-\sum\limits_{k=0}^{j-1}f[i][n][k]\),那因此咱们只要对于第\(c\)种钞票维护\(f[c][i][j]\)就好,而后维护一个\(sum[c]=\sum\limits_{k=0}^{p_c}f[c][n][k]\),每次更新完\(f[c]\)以后把\(f[c][n][p_c]\)加到\(sum[c]\)里面就行了
接下来就是空间怎么解决:对于\(f[c][i][j]\)来讲,咱们是按照\(j\)进行dp的,显然\(j\)那维能够滚动掉,\(f[c][][j]\)到\(f[c][][j+1]\)的更新是\(O(n)\)的,而后咱们就得到了一个\(O(nm)\)的算法
mark:(弱智操做)谁告诉你听算指望的时候能够钦定顺序的???
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=3010,M=310; double sum[N],f[2][M][N]; double a[N][M]; int now[M],pre[M]; int n,m; double ans; void dp(int x){ swap(now[x],pre[x]); int Now=now[x],Pre=pre[x]; f[Now][x][0]=0; for (int i=1;i<=n;++i) f[Now][x][i]=f[Now][x][i-1]*(1-a[i][x])+f[Pre][x][i-1]*a[i][x]; } int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif int x; scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) scanf("%d",&x),a[i][j]=1.0*x/1000; for (int i=1;i<=m;++i){ f[0][i][0]=1; for (int j=1;j<=n;++j) f[0][i][j]=f[0][i][j-1]*(1-a[j][i]); sum[i]=f[0][i][n]; } for (int i=1;i<=m;++i) now[i]=0,pre[i]=1; int which; double mx; for (int j=1;j<=n;++j){ mx=0; which=-1; for (int i=1;i<=m;++i) if (1-sum[i]>mx) which=i,mx=1-sum[i]; ans+=mx; dp(which); sum[which]+=f[now[which]][which][n]; } printf("%.10lf\n",ans); }