2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) -E. Explosion Exploit- probability shaped pressure dp +

2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018) -E. Explosion Exploit- probability shaped pressure dp +


【Problem Description】

We have \ (n \) personal, the other party \ (m \) individuals, each person has a health value \ (H_i \) , there \ (d \) attacks, each randomly selected from everyone \ ( 1 \) individuals, to reduce its \ (1 \) health value. Ask each other all probability will eliminate how much?

【Solution】

Methods \ (1 \) :

The health value of all as a state, because \ (h \) up to \ (6 \) , so the value of the health status of all people can be by \ (7 \) hexadecimal number \ (hash \) to an integer, and stored in order to transfer state. Assuming that the current state \ (hash \) is \ (State \) , health value greater than \ (0 \) number of people have \ (sz \) months, to enumerate all who might choose, for one of them to make it health value minus \ (1 \) state is \ (nextstate \) , there \ (DP [nextstate] DP = [state] \ Times \ FRAC SZ} {1} {\) . After \ (d \) after the time all the states, the other side is the number \ (0 \) probability can add up.

But there are several issues:

  1. Up there \ (10 \) individuals, it may be necessary \ (7 ^ {10} = 3 \ times 10 ^ 8 \) to save the state, keep it down, but you can find that store state when the health of all irrespective of the size of the order of values, so that they can be forced to guarantee orderly. So that only \ (2 \ ^ 10. 5 Times \) .
  2. Converted to \ (7 \) hex and each state after its sort, and ultimately no way to know who is the other person. Yes, the other side of the person's health takes a negative value in saved time, so after ordering other people must ranked in the forefront. Decimal conversion also changed to \ (13 \) band, and increase value for everyone's health (6 \) \ offset value, to ensure the healthy values are positive. So long as the final judgment about whether the forefront of people's health is greater than \ (0 \) can be.

Methods \ (2 \) :

Classified according to the number of health value, health statistics for each value of (one's own and other people's separate statistics), then \ (6 \) kinds of health value corresponding number also by \ (7 \) hexadecimal number \ (hash \ ) to an integer, plus other people need a total of up \ (12 \) bits, the number of storage and high health value of each other, so you can judge by \ (state \) is less than equal to \ (all = (666666 ) _7 \) , to determine whether the other people who were all eliminated.

Then is transferred, health enumeration value \ (i \) , each value from health \ (i \) of people in the election \ (1 \) individuals, its health value minus \ (1 \) , select the value of health is \ (I \) probability that a person is \ (\ FRAC {H [I] SUM {}} \) , where \ ([i] h \) , for the health value \ (I \) number, \ (sum \) is the total number. So there are \ (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;
}

Guess you like

Origin www.cnblogs.com/--Simon/p/11668804.html