2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)-E. Explosion Exploit-概率+状压dp


【Problem Description】

我方有\(n\)个人,对方有\(m\)个人,每个人都有一个健康值\(h_i\),有\(d\)次攻击,每次随机从所有人中选\(1\)个人,减少其\(1\)健康值。问将对方所有人都消灭的概率是多少?

【Solution】

方法\(1\):

​ 将所有人的健康值作为一种状态,因为\(h\)最大为\(6\),所以可以将所有人的健康值状态通过\(7\)进制数\(hash\)成一个整数,便于保存以及状态的转移。假设目前的状态\(hash\)值为\(state\),健康值大于\(0\)的人数有\(sz\)个,枚举所有可能选择的人,对于其中一个人,使其健康值减\(1\)后的状态为\(nextstate\),则有\(dp[nextstate]=dp[state]\times \frac{1}{sz}\)。经过\(d\)次之后所有状态中,将对方人数为\(0\)的概率加起来即可。

但是有几个问题:

  1. 最多会有\(10\)个人,所以可能需要\(7^{10}=3\times 10^8\)来保存状态,存不下,但是可以发现,存储状态的时候,与所有人的健康值的大小顺序无关,所以可以强制保证他们有序。这样只需要\(2\times 10^5\)。
  2. 转换为\(7\)进制且每个状态都对其排序后,最终无法知道那些人是对方的人。可以在保存的时候,将对方的人的健康值取负,这样排序后对方的人一定排在最前端。转换进制也改变为\(13\)进制,且对于每个人的健康值增加\(6\)的偏移值,保证健康值都是正数。这样最终只要判断一下最前端的人的健康值是否大于\(0\)即可。

方法\(2\):

​ 根据健康值分类,统计每种健康值的人数(己方与对方的人分开统计),然后将\(6\)种健康值对应的人数也通过\(7\)进制数\(hash\)到一个整数上,加上对方的人总共最多需要\(12\)位,且高位存储对方每种健康值的人数,这样可以通过判断\(state\)是否小于等于\(all=(666666)_7\),来确定对方的人是否全被消灭。

然后就是转移,枚举健康值\(i\),每次从健康值为\(i\)的人里选\(1\)个人,将其健康值减\(1\),选择健康值为\(i\)的人的概率为\(\frac{h[i]}{sum}\),其中\(h[i]\)为,健康值为\(i\)的人数,\(sum\)为总人数。所以有\(dp[state]=dp[nextstate]*\frac{h[i]}{sum}\)。


【Code】

//方法1
#include<iostream>
#include<iomanip>
#include<map>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define int long long
map<int,double>dp; //dp[state]表示从初始状态转换到状态state的概率为dp[state]
vector<int>v;
int encode(vector<int>v){ //hash为13进制
int ans=0,tmp=1;
for(auto vv:v){
ans+=(vv+6)*tmp; //增加6的偏移值,保证都为正数
tmp*=13;
}
return ans;
}
vector<int> decode(int state){ //求每个人的健康值
v.clear();
while(state){
v.push_back(state%13-6);
state/=13;
}
sort(v.begin(),v.end()); //保证有序
return v;
}
map<int,double> solve(){
map<int,double>mp;
for(auto v:dp){
vector<int>state=decode(v.first);
for(int i=0;i<state.size();i++){ //枚举每种选择,转移状态
vector<int>copy(state);
if(copy[i]>0) copy[i]--;
else if(copy[i]<0) copy[i]++;
if(copy[i]==0){ //将健康值为0的人去除
copy.erase(copy.begin()+i);
}else{
sort(copy.begin(),copy.end()); //保证健康值有序
}
mp[encode(copy)]+=v.second*1.0/state.size();
}
}
return mp;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n,m,d,sum1=0;cin>>n>>m>>d;
for(int i=1;i<=n;i++){
int x;cin>>x;sum1+=x;
v.push_back(x);
}
int sum2=0;
for(int i=1;i<=m;i++){
int x;cin>>x;sum2+=x;
v.push_back(-x); //将对方的人健康值取负,以在最终的时候区别我方/对方
}
if(sum2>d){ //无法将对方所有人都消灭
cout<<"0"<<endl;
return 0;
}
if(sum1+sum2<=d){ //可将所有人都消灭
cout<<"1"<<endl;
return 0;
}
sort(v.begin(),v.end()); //保证有序
dp[encode(v)]=1;
for(int i=0;i<d;i++) dp=solve(); //转移d次
double ans=0;
for(auto v:dp){
vector<int>t=decode(v.first);
if(t.size()&&t[0]>0) ans+=v.second; //如果最小的人的健康值都大于0,则对方的人一定都被消灭
}
cout<<setiosflags(ios::fixed)<<setprecision(9);
cout<<ans<<endl;
return 0;
}
//方法2
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
#define maxn 10
#define INF 0x3f3f3f3f
#define int long long
int h1[maxn]/*己方健康值为i的人数*/,h2[maxn]/*对方健康值为i的人数*/,all=0/*临界值*/;
int encode(){
int ans=0;
for(int i=1;i<=6;i++) ans=ans*7+h2[i]; //对方放在高6位
for(int i=1;i<=6;i++) ans=ans*7+h1[i]; //己方放在低6位
return ans;
}
map<int,double>dp;
double dfs(int pos,int state){
if(dp.count(state)) return dp[state];
double &ans=dp[state],sum=0;
if(state<=all) return ans=1; //如果高6位全为0,则对方全被消灭
if(pos==0) return ans=0;
for(int i=1;i<=6;i++){
if(!h1[i]) continue;
sum+=h1[i]; //总人数
h1[i]--;h1[i-1]++; //当前血量的人减少一个,前一个血量的人增加一个,使得总血量只减少了1
ans+=(h1[i]+1)*dfs(pos-1,encode());
h1[i]++;h1[i-1]--; //注意恢复
}
for(int i=1;i<=6;i++){
if(!h2[i]) continue;
sum+=h2[i];
h2[i]--;h2[i-1]++;
ans+=(h2[i]+1)*dfs(pos-1,encode());
h2[i]++;h2[i-1]--;
}
ans/=sum; //最后除以总人数
return ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n,m,d;cin>>n>>m>>d;
for(int i=1;i<=n;i++){
int x;cin>>x;h1[x]++;
}
for(int i=1;i<=m;i++){
int x;cin>>x;h2[x]++;
}
for(int i=1;i<=6;i++) all=all*7+6; //低6位全满时的最大值
cout<<setiosflags(ios::fixed)<<setprecision(9);
cout<<dfs(d,encode())<<endl;
return 0;
}

2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)-E. Explosion Exploit-概率+状压dp的更多相关文章

  1. (寒假GYM开黑)2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)

    layout: post title: 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) author: &qu ...

  2. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)- D. Delivery Delays -二分+最短路+枚举

    2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)- D. Delivery Delays -二分+最短路+枚举 ...

  3. 2019年湖南多校第一场||2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)

    第一场多校就打的这么惨,只能说自己太菜了,还需继续努力啊- 题目链接: GYM链接:https://codeforces.com/gym/101933 CSU链接:http://acm.csu.edu ...

  4. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) - 4.28

    赛后补了几道 赛中我就写了两个... A - Altruistic AmphibiansGym - 101933A 看了眼榜没几个人做.就没看. 最后发现就是一个DP(但是我觉得复杂度有点迷) 题意: ...

  5. Gym .101933 Nordic Collegiate Programming Contest (NCPC 2018) (寒假gym自训第四场)

    (本套题算是比较温和吧,就是罚时有点高. B .Baby Bites 题意:给出一个婴儿给出的数组,有一些数字听不清楚,让你还原,问它是否是一个从1开始的一次增加的数组. 思路:从左往右依次固定,看是 ...

  6. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) Solution

    A. Altruistic Amphibians Upsolved. 题意: $有n只青蛙,其属性用三元组表示 <l_i, w_i, h_i> l_i是它能跳的高度,w_i是它的体重,h_ ...

  7. [十一集训] Day1 (2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018))

    A Altruistic Amphibians 原题 题目大意: n只青蛙在高度为d的井中,每只有跳跃距离.重量和高度,每只青蛙可以借助跳到别的青蛙的背上而跳出井,每只青蛙能承受的最大重量是自身重量, ...

  8. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) D. Delivery Delays (二分+最短路+DP)

    题目链接:https://codeforc.es/gym/101933/problem/D 题意:地图上有 n 个位置和 m 条边,每条边连接 u.v 且有一个距离 w,一共有 k 个询问,每个询问表 ...

  9. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) A. Altruistic Amphibians (DP)

    题目链接:https://codeforc.es/gym/101933/problem/A 题意:有 n 只青蛙在一个坑里面,要求可以跳出坑的青蛙的最大数量.每个青蛙有 3 种属性:l 为青蛙一次可以 ...

随机推荐

  1. Excel四象限散点图的制作方法

    Excel中四象限散点图带文本数据标签,可以在散点图的基础上进行一些设置即可得到,无需第三方插件或者宏代码,非常方便,以office2013为例,效果如下: 步骤: 1.准备好数据源,选中两列数据源( ...

  2. sql多列值一行显示

    select stuff(( select ',' + UserNM from tblSysUser for xml path('')), 1,1,'')

  3. JIT(just in time)即时编译器

    JIT(just in time) 前端vs后端 在编译原理中,通常将编译分为前端和后端.其中前端会对程序进行词法分析.语法分析.语义分析,然后生成一个中间表达形式(称为IR:Intermediate ...

  4. C#不区分大小写的字符串替换(Replace)函数

    在.NET中,不调用C++/CLI,进行字符串替换有好几种方法: 1.最常用的,就是String实例.Replace(),但这个不能忽略大小写. 2.System.Text.Regex(Regular ...

  5. 【计算机视觉】目标检测中的指标衡量Recall与Precision

    [计算机视觉]目标检测中的指标衡量Recall与Precision 标签(空格分隔): [图像处理] 说明:目标检测性能指标Recall与Precision的理解. Recall与Precision ...

  6. C++ ActiveX开发的问题讨论

    最近在一个项目中需要开发一个ocx插件,在开发过程中发现了一些问题,所以在此记录一下. 我想讨论的主要是函数的参数问题,我分别使用c++,JavaScript,C#对ocx插件做了测试,发现不同的参数 ...

  7. 配置tomcat 加载指定的jar

    # vi bin/catalina.sh

  8. 图片url地址的生成获取方法

    在写博客插入图片时,许多时候需要提供图片的url地址.作为菜鸡的我,自然是一脸懵逼.那么什么是所谓的url地址呢?又该如何获取图片的url地址呢? 首先来看一下度娘对url地址的解释:url是统一资源 ...

  9. 使用PHP开发HR系统(6)

        本节讲述如何连接Postgre数据库并查询与显示数据. ==================================================================== ...

  10. 3D星形贴图

    3D星形贴图: /** * * *---------------------* * | *** 3D星形贴图 *** | * *---------------------* * * 编辑修改收录:fe ...