2016ACM青岛区域赛-重现赛(部分题解)

好多题都是数学题。。。看的头晕眼花。。。。连一个网络流都得用数学方法改变一下去计算。。。

各种概率。。期望。。只能硬着头皮去写。。看来要早点学数论了。。。。

A Relic Discovery

水题。。。看代码就能懂这题是啥意思了。。。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,t;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        int sum = 0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            sum += a*b;
        }
        printf("%d\n",sum);
    }
    return 0;
}

D.Lucky Coins

题意就是告诉你有 n 种不同的硬币,每种硬币有 k 个,同时抛这种硬币有 (p*100)% 的概率正面向上 ,现在开始抛所有硬币,如果硬币背面朝上就被淘汰掉,直到所有硬币都被淘汰掉,就结束抛硬币,而我们在抛硬币时 最后的被淘汰的一种硬币,就被我们称做 幸运硬币,现在求每种硬币成为 幸运硬币 的概率。。

刚开始看了一下。。感觉这道题。。似乎依靠计算。。

举个例子假设某一个硬币 被我们抛 k 次仍然正面朝上(也就是还存活)的概率 p0 = p ^ k

那么这个事件的相反时间就是 保证 k 次内淘汰这枚硬币,这个概率 p0 ' = 1 - p0

那么如果有 n 个硬币,保证 k 次内全被淘汰掉的概率 ,很显然是叠加的事件而不是并列的,总概率 P = (1 - p ^ k ) ^ n

那么保证某个硬币 活下 k 次概率恰好就是上一个事件相反事件 P ' = 1 - P;

那么对于某一个硬币成为幸运硬币概率 

 =   Ps'k * ( P1(k-1) * P2(k-1) * P3(k-1) ..... Pi(k-1) ) (k != s)

也就是保证 前 k-1 次内 除了这种硬币 其他硬币全被淘汰掉,而这个硬币能幸存下来,但是还要想一下:

既然投了 k 次还剩下这一种硬币,那么低 k+1次 必须这种硬币也得被淘汰掉,

那么 我们在原有的基础上 减去 Ps'(k+1) * ......(这里同上) 不就解决了吗。。

既然模型构建好了。我们想一下如何代码实现,

开一个二维数组 die[ i ][ k ] 表示 i 种硬币 k 步内被全部淘汰概率,liv[ i ] [ k ],则表示反事件。

die[ i ] [ k ] = (1 - p^k) ^ n

liv[ i ] [ k ] = 1 - die[ i ][ k ]

最终每种硬币的幸运概率也就算出来了。。。。。

感觉这种解决方法很像 dp

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define dou double
using namespace std;
const int maxn = 1000 + 5;
dou die[maxn][maxn];
dou liv[maxn][maxn];
dou ans[maxn];
int t,n;
dou poww(dou a, int t)
{
    dou ans = 1;
    while(t)
    {
        if(t&1)
            ans *= a;
        a *= a;
        t >>= 1;
    }
    return ans;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int s;
            dou p;
            scanf("%d%lf",&s,&p);
            dou temp = p;
            for(int j=1;j<=100;j++)
            {
                die[i][j] = poww(1.0 - temp, s);
                liv[i][j] = 1.0 - die[i][j];
                temp *= p;
            }
        }
        for(int i=1;i<=n;i++)
        {
			dou ant = 0;
			for(int k=1;k<=99;k++)
            {
				double te = 1;
				for(int j=1;j<=n;j++)
                {
					if(j == i)continue;
					te *= die[j][k];
				}
				ant += te * (liv[i][k]-liv[i][k+1]);
			}
			ans[i] = ant;
		}
        if(n == 1)
            printf("%.6lf\n",1.0);
        else
            for(int i=1;i<=n;i++)
                printf("%.6lf%c",ans[i],i==n?'\n':' ');
    }
    return 0;
}

H.Coding Contest

题意如下

有 n 个点 m 条边,每个点 有 a 个人 b 张餐桌,每条边有一定容量,而且从第二个人开始之后就有 p 几率在走这条路的时候把这条路走坏掉。。现在到了午饭时间,每个人都需要去找一个餐桌进餐,求整个图中网络不被破坏的概率。。。

先不看那个神奇的破坏掉路程的概率 p ,你就会发现这就是个最大流啊

人多桌子少,自然要往外走,人少桌子多,自然需要别人再过来进餐。

那么建图就很容易了

S   ----人桌差值---- (人多桌少的点) ---输入的容量--- (人少桌多的点) ---人桌差值--- T 

那么既然本题还涉及到了一个破坏路线的概率,那么我们不如把他当做一个限制用的数值(当然我们不能模拟去直接破坏)

第一个人没问题,第二个人第三个人一次走过去的话,不被破坏的概率就是 (1-p)*(1-p)*(1-p)......

是一个乘法关系,我们自然希望这个概率越小越好,那么这不就是一个最小费用最大流吗?

但是问题来了,是乘法关系我们不能直接相加,我们不如对概率 p 取 log

把边的费用定位 log( 1 - p ),这样的话 跑一下最大费用最大流(费用设为负数),算出最小费用就是不被破坏的概率了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 100007;
const double eps = 1e-8;
struct node
{
    double cost;
    int v,cap,next;
}ed[maxn];
int tot,head[maxn];
void add(int u, int v, int cap, double cost)
{
    ed[tot].v=v;
    ed[tot].cost=cost;
    ed[tot].cap=cap;
    ed[tot].next=head[u];
    head[u]=tot++;
    ed[tot].v=u;
    ed[tot].cost=-cost;
    ed[tot].cap=0;
    ed[tot].next=head[v];
    head[v]=tot++;
}
int pre[maxn],m,n,N;
double dis[maxn];
bool vis[maxn];
bool SPFA(int s, int t)
{
    queue<int> q;
    for(int i=0; i<N; ++i)
    {
        dis[i]=INF;
        vis[i]=0;
        pre[i]=-1;
    }
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(q.size())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u]; i+1; i=ed[i].next)
        {
            int v = ed[i].v;
            if(ed[i].cap>0 && dis[v]-dis[u]-ed[i].cost>eps)//松弛的时候别忘了精度
            {
                dis[v]=dis[u]+ed[i].cost;
                pre[v]=i;
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return pre[t] != -1;
}
double dfs(int s, int t)
{
    int flow=0;
    double cost=0;
    while(SPFA(s,t))
    {
        int minn=INF;
        for(int i=pre[t]; i+1; i=pre[ ed[i^1].v ])
            if(minn>ed[i].cap)
                minn=ed[i].cap;
        for(int i=pre[t]; i+1; i=pre[ ed[i^1].v ])
        {
            ed[i].cap -= minn;
            ed[i^1].cap += minn;
            cost += ed[i].cost*1.0*minn;
        }
        flow += minn;
    }
    return cost;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        tot=0;
        memset(head,-1,sizeof head);
        int a,b,c;
        double d;
        scanf("%d%d",&n,&m);
        N=n+2;
        for(int i=1; i<=n; ++i)
        {
            scanf("%d%d",&a,&b);
            int x=a-b;
            if(x>0)//人比饭桌多
                add(0,i,x,0);
            if(x<0)//饭桌比人多
                add(i,n+1,-x,0);
        }
        for(int i=0; i<m; ++i)
        {
            scanf("%d%d%d%lf",&a,&b,&c,&d);
            if(c>0)//如果只走一个人不需要看 p
                add(a,b,1,0);
            if(c>1)
                add(a,b,c-1,-log2(1.0-d));
        }
        double ans = 1.0 - pow(2,-dfs(0,n+1));//把 log 还原
        printf("%.2f\n",ans);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值