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. 使用Fiddler工具发送post请求(带有json数据)以及get请求(Header方式传参)

    Fiddler工具是一个http协议调试代理工具,它可以帮助程序员测试或调试程序,辅助web开发. Fiddler工具可以发送向服务端发送特定的HTTP请求以及接受服务器回应的请求和数据,是web调试 ...

  2. 分享一下我的个人微信小程序

    分享一下我的个人微信小程序 1.有我平时整理的一些小程序相关的技术,供大家参考. 2.有几个好玩的例子 有问题可以一起参考

  3. 从Asp .net到Asp core (第二篇)《Asp Core 的生命周期》

    前面一篇文章简单回顾了Asp .net的生命周期,也简单提到了Asp .net与Asp Core 的区别,我们说Asp Core不在使用Asp.netRuntime,所以它也没有了web程序生命周期中 ...

  4. ES6新增的一些特性

    1.let关键字,用来代替 var的关键字,特点: 1.变量不允许被重复定义 2.不会进行变量声明提升 3.保留块级作用域中i的 2.const定义常量,特点:1.常量值不允许被改变 2.不会进行变量 ...

  5. Kafka工具教程 - Apache Kafka中的2个重要工具

    1.目标 - 卡夫卡工具 在我们上一期的Kafka教程中,我们讨论了Kafka Workflow.今天,我们将讨论Kafka Tool.首先,我们将看到卡夫卡的意义.此外,我们将了解两个Kafka工具 ...

  6. 《TCP/IP - TCP/UDP》

    一:概述 - 由于 IP 的传输是无状态的,IP 提供尽力服务,但并不保证数据可以到达主机. - 所以,数据的完整性需要更上层的 传输层来保证.TCP和UDP 均属于 传输层. 二:UDP - 特点 ...

  7. Python规范:代码规范要注意

    主要有以下两种代码规范 <8 号 Python 增强规范>(Python Enhacement Proposal #8),以下简称 PEP8: <Google Python 风格规范 ...

  8. 数据分析——matplotlib的用法

    Matplotlib是一个强大的Python绘图和数据可视化的工具包.数据可视化也是我们数据分析的最重要的工作之一,可以帮助我们完成很多操作,例如:找出异常值.必要的一些数据转换等.完成数据分析的最终 ...

  9. Linux系统模拟发送HTTP的get和post请求

    一.get请求: 1.使用curl命令: curl “http://www.baidu.com” 如果这里的URL指向的是一个文件或者一幅图都可以直接下载到本地 curl -i “http://www ...

  10. CCF 2016-12-1 最大波动

    CCF 2016-12-1 最大波动 题目 问题描述 小明正在利用股票的波动程度来研究股票.小明拿到了一只股票每天收盘时的价格,他想知道,这只股票连续几天的最大波动值是多少,即在这几天中某天收盘价格与 ...