• [计蒜客] 矿石采集【记搜、Tarjan缩点+期望Dp】


    Online Judge计蒜客信息学3月提高组模拟赛

    Label:记搜,TarJan缩点,树状数组,期望Dp

    题解

    整个题目由毫无关联的两个问题组合成:

    part1

    问题:对于每个询问的起点终点,求出起点到终点能够收获的最大矿石价值。

    思路:

    1.矿石种类不多,且收获价值时只与是否拥有有关,与收集数量无关,可以用状态压缩表示当前收集矿石的状态;

    2.由于是单向边且可能形成环,用(Tarjan)缩点,然后进行转移。或者直接记忆化dfs/bfs,用(dp[i][j][sta])表示,从(i)(j),是否有可能收集到状态为(sta)的矿石。最后求一遍最大值即可。

    part2

    问题:安排执行哪些委托,使得期望获利最大。

    思路:

    1.整理委托的信息:

    对于一份委托((yi,si,ti,pi,ci,li)),我们可以根据((si,ti))以及前一个部分的预处理来计算出这份委托的收益(还没减去成本)(wi);这份委托的开始时间和结束时间((yi,yi+li)),这里简记为((l,r))。这样我们可以用一个结构体表示一份委托{(l,r,w,c,p)}。

    2.确定转移顺序

    毫无疑问先根据开始时间(li)排序所有委托。

    定义状态(dp[i]),表示已经考虑了(i..q)的委托,且接受了委托(i)时的最大期望获利。接着只用倒序转移即可。对于当前的委托(i),我们利用二分查找(O(logq))的时间内,找到第一个开始时间大于等于当前委托结束时间的委托(j)。然后转移方程为:(dp[i]=max(dp[j...q]+w_i)*(1-p_i)-c_i),这个很好理解就不解释了,而区间([j,q])的最大值可以用树状数组/线段树维护。

    这题就结束了,主要难点是题目变量比较多,只要理清关系分成两个问题求解就好了。

    part1的做法比较多,不过由于数据范围小都可行。part2的时间复杂度为(O(NlogN))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=110,M=210,Q=1e5+10;
    int val[1030];
    int n,m,k,q,has[N];
    
    struct edge{int to,nxt;}e[M];
    int head[N],cnt;
    inline void link(int u,int v){
    	e[++cnt].to=v;e[cnt].nxt=head[u];
    	head[u]=cnt;
    }
    
    bool ok[N][N][1030];
    void dfs(int s,int x,int sta){
    	if(ok[s][x][sta])return;
    	ok[s][x][sta]=1;
    	for(int i=head[x];i;i=e[i].nxt){
    		int y=e[i].to;dfs(s,y,sta|has[y]);
    	}
    } 
    struct Node{
    	int l,r,w,c;
    	double p;
    }x[Q];
    inline bool cmp(Node a,Node b){return a.l<b.l;}
    void pre(){
    	for(register int i=1;i<=n;i++)dfs(i,i,has[i]);
    	for(register int i=1,u,v;i<=q;i++){
    		scanf("%d%d%d%lf%d%d",&x[i].l,&u,&v,&x[i].p,&x[i].c,&x[i].r);
    		x[i].r+=x[i].l;
    		for(register int o=0;o<(1<<k);o++)if(ok[u][v][o]){
    			if(val[o]>x[i].w)x[i].w=val[o];
    		}
    	}
    }
    double c[Q];
    inline double ask(int x){
    	double res=0;
    	while(x<=q){res=max(res,c[x]);x+=x&(-x);}
    	return res;
    }
    inline void update(int x,double d){
    	while(x){c[x]=max(c[x],d);x-=x&(-x);}
    }
    inline void Dp(){
    	sort(x+1,x+q+1,cmp);
    	for(register int i=q;i>=1;i--){ 
    		//如果接受i委托,下一个最早能进行的委托是id 
    		int l=i+1,r=q,id=q+1;
    		while(l<=r){
    			int mid=l+r>>1;
    			if(x[i].r<=x[mid].l)r=mid-1,id=mid;
    			else l=mid+1;
    		}
    		double now=(ask(id)+x[i].w)*(1.0-x[i].p)-x[i].c;
    		update(i,now);
    	}
    	printf("%.6f
    ",ask(1));
    }
    int main(){
    	scanf("%d%d%d%d",&n,&m,&k,&q);
    	for(register int i=1;i<=n;i++){
    		char s[12];scanf("%s",s);
    		for(int j=0;j<strlen(s);j++)if(s[j]=='1')has[i]|=1<<j;
    	}
    	for(register int i=1;i<=m;i++){
    		int u,v;scanf("%d%d",&u,&v);
    		link(u,v);
    	}
    	for(register int i=0,w;i<k;i++){
    		scanf("%d",&w);
    		for(int j=0;j<(1<<k);j++)if((1<<i)&j)val[j]+=w;
    	}
    	pre();	
    	Dp();
    }
    
  • 相关阅读:
    Centos7.2 下搭建LNMP环境(终极版)Yum安装
    ThinkPHP3.2 插入数据库数据,缓存问题
    无限极分类的JS实现
    PHP 商城无限极分类
    高速下载Centos的地址
    高性能的城市定位API接口
    阿帕奇配置本地虚拟站点,XAMPP环境下
    Nginx 下配置Laravel 错误404
    laravel Redis缓存
    pytest.1.快速开始
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11487153.html
Copyright © 2020-2023  润新知