SRM548 Div1Medium KingdomAndDice

来源:互联网 发布:使命召唤12中文优化 编辑:程序博客网 时间:2024/06/08 14:38

【分析】
dp不解释。
首先我们考虑答案的组成:对于a数组中非零的元素,它对答案(获胜的概率)的贡献是已经确定的。对于那些需要修改的元素,如果不考虑任何的优化,我们需要将枚举改成1~+oo,然后再for一遍b数组,算出它对答案的贡献,之后才会考虑dp转移的事情。
那我们先考虑第一个优化:
将b数组排序。
对于[b[i]+1,b[i+1]-1],a改成其中任意一个数字,对答案的贡献是相同的。
不过要记得在这个区间到底有多少个能用(没有与a中非零的重复)。hav为能用几个。
同时注意考虑边界吧。
既然我们已经知道了这个数字对答案的贡献,就开始转移吧。
定义dp[i][j]表示用了i个0,赢了j把。
那么转移是比较简单的:dp[i+1][j+cnt]|=dp[i][j];
接下来就是多重背包的事了。循环hav次。

【代码】

#include <vector>#include <list>#include <map>#include <set>#include <queue>#include <deque>#include <stack>#include <bitset>#include <algorithm>#include <functional>#include <numeric>#include <utility>#include <sstream>#include <iostream>#include <iomanip>#include <cstdio>#include <cmath>#include <cstdlib>#include <ctime>#include<string.h>#define M 55using namespace std;bool dp[M][M*M];int n,x,ko,cnt;int a[M],b[M];int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)scanf("%d",&a[i]);    for(int i=1;i<=n;i++)scanf("%d",&b[i]);    scanf("%d",&x);    memset(dp,0,sizeof(dp));    for(int i=1;i<=n;i++){        ko+=a[i]==0;        for(int j=1;j<=n;j++)cnt+=a[i]>b[j];    }    sort(b+1,b+n+1);    int N=n*n;    for(int i=0;i<=ko;i++)dp[i][cnt]=1;    for(int i=1;i<=n;i++){        int l=b[i]+1;        int r=i==n?x:b[i+1]-1;        int hav=r-l+1;        for(int j=1;j<=n;j++){            if(a[j]>=l&&a[j]<=r)hav--;        }        hav=min(hav,ko);        for(int j=0;j<hav;j++){            for(int k=ko-1;k>=0;k--){                for(int p=0;p<=N;p++){                    if(dp[k][p]&&p+i<=N){                        dp[k+1][p+i]=1;                    }                }            }        }    }    int m=n*n,ans=cnt;    for(int p=0;p<=N;p++){        for(int i=0;i<=ko;i++){            if(dp[i][p]&&abs(n*n-p*2)<m){                ans=p;                m=abs(n*n-p*2);            }        }    }    printf("%.10f\n",1.0*ans/N);    return 0;}
2 0
原创粉丝点击