zoukankan      html  css  js  c++  java
  • ICPC World Finals 2018部分题目解析

    #6403. 「ICPC World Finals 2018」赶飞机

    description

    给定一个(n)个点(m)条边的图,每条边都只能从(a)(b)且必须在第(s)秒时出发第(t)秒到达,然而有(p)的概率不通行
    注意你可以在节点等待,同时你只有到第(s)秒后你才会知道它是否通行
    现在是(0)时刻你在(0)节点,询问在不超过(k)秒的时间内到达(1)节点的最大概率是多少

    data range

    (N,Mle 10^6)
    (s,t,kle 10^{18})

    solution

    唉,果然是太菜了,竟然没有反应过来是一道(dp)
    根据题目描述中错综复杂的情况以及要求出奇怪的最大概率,不难想到用(dp)
    (f_{a,s})表示在(s)时刻到达(a)节点时的最大概率,那么考虑逆推,不难想到

    [f_{a,s}=max(f_{a,nxt_s},p imes f_{b,nxt_t}+(1-p) imes f_{a,nxt_s}) ]

    大概就是说考虑决策:走或者留
    如果选择走还要考虑如果发现当前边不通行了需要继续留着
    不难发现需要把输入的所有边按照起始时间从大到小排,那么如上递推就能保证先前的点已经求好
    但是注意时间很大,用普通的数组存肯定存不下
    考虑使用(map),这样的话查找(nxt_s)的时候可以用( ext{upper_bound})十分方便

    time complexity

    (mathcal O(mlog_2m))

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    typedef long long ll;
    struct bus{int a,b;ll s,t;double p;}a[N];
    inline bool cmp(const bus&x,const bus&y){return x.s>y.s;}
    int n,m;ll k;
    map<ll,double>f[N];
    inline double work(int u,ll t)
    {
    	return f[u].upper_bound(t)->second;
    }
    int main()
    {
    	scanf("%d%d%lld",&m,&n,&k);
    	for(int i=1;i<=m;++i)
    	{
    		bus&d=a[i];
    		scanf("%d%d%lld%lld%lf",&d.a,&d.b,&d.s,&d.t,&d.p);
    	}
    	f[1][k+1]=1;
    	sort(a+1,a+m+1,cmp);
    	for(int i=1;i<=m;++i)
    	{
    		bus&d=a[i];
    		double p1=work(d.a,d.s),p2=d.p*work(d.b,d.t)+(1-d.p)*p1;
    		f[d.a][d.s]=max(f[d.a][d.s],max(p1,p2));
    	}
    	printf("%.10lf",f[0].begin()->second);
    	return 0;
    }
    

    #6413. 「ICPC World Finals 2018」无线光纤

    description

    给定一个(n)个点(m)条边的图,要求出它的一棵生成树,使得生成树和原图点的度数不同的尽可能少,要求输出方案

    data range

    (nle 10^4,mle 10^5)

    solution

    非常有趣的一道题目
    容易发现生成树的度数之和为(2(n-1))
    可以证明生成树所有点之间的度数可以任意调配,只要满足和为(2(n-1))且任意节点度数不小于(1)
    比如说我们要让(u)少一个度(v)多一个度
    可以将(u)作为根,然后断掉(v)所在的儿子子树的边
    而后从(v)引出一条边连到(u)的另一个儿子上即可
    剩下的就好做了
    注意到总数一定
    我们只需要将原图的点的度数从小到大排序,先满足度数较小的,这样显然可以满足尽量多的点,剩下的不满足的自然就是最少的
    现在的问题就只在于如何在度数数组的基础上构造出一棵树
    将点的度数从小到大排序,从后往前考虑
    对于每个点,将它和它的前一个点连边
    然后再从前往后地对每一个度数不为零的点连边,直到度数耗尽
    为什么这样做是正确的?
    考虑如果有一个度数为(k)的点,那么根据和为(2(n-1))的性质,必然会至少有(k-1)个度数为(1)的点
    进行一次如此的连边后,不难发现是最后一个点作为父亲连接着若干叶子和一个不为叶子的节点
    而剩下的不难发现就是原问题的子问题,如此做下去必然是正确的

    time complexity

    (mathcal O(nlog_2n))

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e4+5;
    typedef pair<int,int> pii;
    int n,m,d[N];
    pii a[N],b[N];
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1,x,y;i<=m;++i)
    	{
    		scanf("%d%d",&x,&y);++x,++y;
    		++d[x],++d[y];
    	}
    	for(int i=1;i<=n;++i)a[i]=make_pair(d[i],i);
    	sort(a+1,a+n+1);int tot=(n-1)<<1,p=1;
    	for(int i=1;i<=n;++i)b[i].first=1,b[i].second=a[i].second,--tot;
    	while(tot>=a[p].first-1&&p<=n)b[p]=a[p],tot-=a[p].first-1,++p;
    	if(tot)b[p].first+=tot;
    	printf("%d
    ",n-p+1);
    	printf("%d %d
    ",n,n-1);
    	sort(b+1,b+n+1);
    	for(int i=n;b[i].first;--i)
    	{
    		int now=b[i].first,u=b[i].second;
    		--now,--b[i-1].first;
    		printf("%d %d
    ",u-1,b[i-1].second-1);
    		for(int j=1;now&&j<=n;++j)
    			if(b[j].first)
    			{
    				--b[j].first,--now;
    				printf("%d %d
    ",u-1,b[j].second-1);
    			}
    	}
    	return 0;
    }
    
  • 相关阅读:
    设计模式之原型模式
    Mac OSx下的APK反编译
    Android中ListView封装收缩与展开
    带密码登录的密码保险箱应用源码
    HTML 5缓存机制:Cache Manifest配置实例
    PHP开发大型项目的一点经验
    Windows Phone 编程: 摇一摇 效果
    swift皮筋弹动发射飞机
    Win8.1应用开发之适配器模式(C#实现)
    java实现身份证校验
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/14224496.html
Copyright © 2011-2022 走看看