好多题都是数学题。。。看的头晕眼花。。。。连一个网络流都得用数学方法改变一下去计算。。。
各种概率。。期望。。只能硬着头皮去写。。看来要早点学数论了。。。。
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;
}