2021 ccpc 哈尔滨 G. Damaged Bicycle 状压 + 期望dp

文章目录

传送门

题意:

你需要从 1 1 1走到 n n n,初始速度是 t t t,某些地方有自行车,每个位置自行车有 p i p_i pi的概率是坏掉的,如果自行车没坏可以骑上自行车,速度是 r r r,可以一直骑着到终点。

1 ≤ t ≤ r ≤ 1 e 4 , 1 ≤ n , m ≤ 1 e 5 , 0 ≤ k ≤ 18 , 1 ≤ a i ≤ n , 0 ≤ p i ≤ 100 1\le t\le r\le 1e4,1\le n,m\le 1e5,0\le k\le 18,1\le a_i\le n,0\le p_i\le 100 1tr1e4,1n,m1e5,0k18,1ain,0pi100

思路:

注意到 k k k很小,可以选择状压一下到哪些有自行车的位置,设 f [ s t a t e ] [ j ] f[state][j] f[state][j]表示当前选择的自行车位置集合为 s t a t e state state,最后一次停在 j j j的时候,到终点的期望,显然我们需要倒着推,转移方程:
f [ s t a t e ] [ i ] = m i n ( f [ s t a t e ] [ i ] , ( f [ s t a t e ∣ ( 1 < < j ) ] [ j ] + d i s [ i ] [ a [ j ] ] / t ) ∗ p [ i ] + d i s [ i ] [ n ] ∗ ( 1 − p [ i ] ) / r ) f[state][i]=min(f[state][i],(f[state|(1<<j)][j]+dis[i][a[j]]/t)*p[i]+dis[i][n]*(1-p[i])/r) f[state][i]=min(f[state][i],(f[state(1<<j)][j]+dis[i][a[j]]/t)p[i]+dis[i][n](1p[i])/r)

让后选择记忆化或者循环都可以,这个题由于有边界问题,显然选 d f s dfs dfs更好写。

记忆化:

#include<bits/stdc++.h>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
using namespace std;

const int N=100010,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;
typedef pair<int,int> PII;

int t,r;
int n,m,k;
vector<PII>v[N];
int dis[21][N];
int a[N],p[N];
bool st[N];

void dijkstra(int s) {
    priority_queue<PII,vector<PII>,greater<PII>>q;
    memset(st,0,sizeof(st));
    memset(dis[s],0x3f,sizeof(dis[s]));
    dis[s][a[s]]=0;
    q.push({0,a[s]});
    while(q.size()) {
        auto u=q.top(); q.pop();
        int id=u.Y;
        if(st[id]) continue;
        st[id]=1;
        for(auto x:v[id]) {
            if(dis[s][x.X]>dis[s][id]+x.Y) {
                dis[s][x.X]=dis[s][id]+x.Y;
                q.push({dis[s][x.X],x.X});
            }
        }
    }
}

double f[1<<20][20],P[1010];
LL d[1<<20][20];

double dfs(int state,int pos) {
    if(f[state][pos]!=-1) return f[state][pos];
    double tmp=P[pos]*dis[pos][n]/t+(1-P[pos])*dis[pos][n]/r;
    for(int i=0;i<k;i++) {
        if(i==pos) continue;
        if(state&(1<<i)) continue;
        tmp=min(tmp,P[pos]*(dfs(state|(1<<i),i)+1.0*dis[pos][a[i]]/t)+(1-P[pos])*dis[pos][n]/r);
    }
    return f[state][pos]=tmp;
}

void solve() {
    scanf("%d%d%d%d",&t,&r,&n,&m);
    while(m--) {
        int a,b,c; scanf("%d%d%d",&a,&b,&c);
        v[a].pb({b,c});
        v[b].pb({a,c});
    }
    scanf("%d",&k); a[k]=1; p[k]=100;
    for(int i=0;i<k;i++) scanf("%d%d",&a[i],&p[i]);
    for(int i=0;i<=k;i++) dijkstra(i);
    if(dis[k][n]==INF) {
        puts("-1");
        return;
    }
    for(int i=0;i<=k;i++) P[i]=1.0*p[i]/100;
    for(int i=0;i<1<<20;i++) for(int j=0;j<20;j++) f[i][j]=-1;
    printf("%.8f\n",dfs(0,k));
}

int main() {
	int _=1;
	while(_--) {
		solve();
	}

}

循环二进制

#include<bits/stdc++.h>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
using namespace std;

const int N=100010,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;
typedef pair<int,int> PII;

int t,r;
int n,m,k;
vector<PII>v[N];
int dis[21][N];
int a[N],p[N];
bool st[N];

void dijkstra(int s) {
    priority_queue<PII,vector<PII>,greater<PII>>q;
    memset(st,0,sizeof(st));
    memset(dis[s],0x3f,sizeof(dis[s]));
    dis[s][a[s]]=0;
    q.push({0,a[s]});
    while(q.size()) {
        auto u=q.top(); q.pop();
        int id=u.Y;
        if(st[id]) continue;
        st[id]=1;
        for(auto x:v[id]) {
            if(dis[s][x.X]>dis[s][id]+x.Y) {
                dis[s][x.X]=dis[s][id]+x.Y;
                q.push({dis[s][x.X],x.X});
            }
        }
    }
}

double f[1<<20][20],P[1010];

void solve() {
    scanf("%d%d%d%d",&t,&r,&n,&m);
    while(m--) {
        int a,b,c; scanf("%d%d%d",&a,&b,&c);
        v[a].pb({b,c});
        v[b].pb({a,c});
    }
    scanf("%d",&k); a[k]=1; p[k]=100;
    for(int i=0;i<k;i++) scanf("%d%d",&a[i],&p[i]);
    for(int i=0;i<=k;i++) dijkstra(i);
    if(dis[k][n]==INF) {
        puts("-1");
        return;
    }
    for(int i=0;i<=k;i++) P[i]=1.0*p[i]/100;
    for(int i=0;i<1<<20;i++) for(int j=0;j<20;j++) f[i][j]=1e18;
    for(int i=(1<<k)-1;i>=1;i--) {
        for(int x=0;x<k;x++) {//当前走到的点
            if(i&(1<<x)) {
                f[i][x]=(1-P[x])*dis[x][n]/r+P[x]*dis[x][n]/t;
                for(int y=0;y<k;y++) {//下一个要到的点
                    if(x==y) continue;
                    if((i&1<<y)) continue;
                    f[i][x]=min(f[i][x],(1-P[x])*dis[x][n]/r+P[x]*(1.0*dis[x][a[y]]/t+f[i|(1<<y)][y]));
                }
            }
        }
    }
    double ans=1.0*dis[k][n]/t;
    for(int y=0;y<k;y++) {
        int x=1<<y,i=0;
        ans=min(ans,1.0*dis[k][a[y]]/t+f[i|(1<<y)][y]);
    }
    printf("%.8f\n",ans);

}

int main() {
	int _=1;
	while(_--) {
		solve();
	}

}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值