coin

Decsription

  數據範圍:\(n<=3000,m<=300\),保證\(\forall i,\sum\limits_{j}p_{ij}=1000\)
  ios

Solution

  平常指望算不對。。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:(弱智操做)誰告訴你聽算指望的時候能夠欽定順序的???

​  

Code

#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);
}
相關文章
相關標籤/搜索