洛谷每日一练5.13--P4316+P1351+P1144(概率DP+最短路+图)

P4316

题意:

给 一 个 D A G , 从 1 出 发 , 每 次 等 概 率 地 走 这 个 点 能 走 的 边 , 给一个DAG,从1出发,每次等概率地走这个点能走的边, DAG1
问 走 到 n 点 的 期 望 路 径 长 度 为 多 少 ? 问走到n点的期望路径长度为多少? n?

思路:

首 先 这 是 一 道 典 型 的 概 率 D P , 用 d p [ i ] 表 示 现 在 在 i 点 到 达 n 点 首先这是一道典型的概率DP,用dp[i]表示现在在i点到达n点 DPdp[i]in
还 需 要 走 的 路 径 长 度 的 期 望 , 那 么 d p [ n ] = 0 , 而 d p [ 1 ] 就 是 答 案 还需要走的路径长度的期望,那么dp[n] = 0,而dp[1]就是答案 dp[n]=0,dp[1]
对 于 一 条 边 对于一条边 u − > v u->v u>v

d p [ u ] = ∑ ( d p [ v ] + w ) d e g [ u ] dp[u] = ∑\frac{(dp[v] + w)}{deg[u]} dp[u]=deg[u](dp[v]+w)

因 为 d p [ n ] = 0 因为dp[n] = 0 dp[n]=0, 且 是 个 D A G 即 后 面 的 值 一 旦 确 定 , 不 会 改 变 且是个DAG即后面的值一旦确定,不会改变 DAG
从 后 面 往 前 推 , 建 反 图 从后面往前推,建反图

#include <cstdio>
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e5+10;
int head[N],idx,deg[N],order[N],k;
struct Node{
	int to,nex;
	double w;
}e[N<<1];
double dp[N];
void add_edge(int u,int v,double w){
	e[idx].to = v;
	e[idx].w = w;
	e[idx].nex = head[u];
	head[u] = idx++;
}
int n,m;
void topo(){
	int in[N],k = 0;
	queue<int> q;
	memset(in,0,sizeof(in));
	for(int i = 1;i <= n;i++){
		in[i] = deg[i];
		if(!in[i]) q.push(i);
	}
	while(q.size()){
		int u = q.front();q.pop();
		order[k++] = u;
		for(int i = head[u];~i;i = e[i].nex){
			int v = e[i].to;
			dp[v] += (dp[u] + e[i].w) / deg[v];
			if(--in[v] == 0) q.push(v);
		}
	}
}
int main(){
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for(int i = 0;i < m;i++){
		int u,v;scanf("%d%d",&u,&v);
		double w;scanf("%lf",&w);
		add_edge(v,u,w);deg[u]++;
	}
	topo();
	printf("%.2lf\n",dp[1]);
	return 0;
}

P1351

题意:

給 一 颗 树 , 边 权 均 为 1 , 求 出 两 点 距 离 为 2 的 两 点 乘 积 之 和 , 給一颗树,边权均为1,求出两点距离为2的两点乘积之和, 12
还 有 乘 积 的 最 大 值 还有乘积的最大值

思路:

枚 举 中 间 点 是 哪 个 点 枚举中间点是哪个点
遍 历 以 这 个 点 为 中 间 的 点 对 : 遍历以这个点为中间的点对:
当 有 两 个 邻 点 时 , 对 答 案 贡 献 为 2 a b = ( a + b ) 2 − ( a 2 + b 2 ) 当有两个邻点时,对答案贡献为2ab = (a+b)^2 - (a^2+b^2) 2ab=(a+b)2(a2+b2)
三 个 邻 点 时 , 对 答 案 贡 献 为 2 a b + 2 a c + 2 b c = ( a + b + c ) 2 − ( a 2 + b 2 + c 2 ) 三个邻点时,对答案贡献为2ab+2ac+2bc = (a+b+c)^2 - (a^2+b^2+c^2) 2ab+2ac+2bc=(a+b+c)2(a2+b2+c2)

均 能 写 成 权 值 和 的 平 方 减 去 平 方 的 和 均能写成权值和的平方减去平方的和
最 大 值 就 看 下 邻 点 权 值 的 最 大 值 和 次 大 值 相 乘 能 不 能 更 新 答 案 最大值就看下邻点权值的最大值和次大值相乘能不能更新答案

#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
const int mod = 1e4+7;
struct Node{
	int to,nex;	
}e[N<<1];
int head[N],idx;
void add_edge(int u,int v){
	e[idx].to = v;
	e[idx].nex = head[u];
	head[u] = idx++;
}
int n,val[N];
int mx,ans;
void solve(){
	//枚举中间点
	for(int u = 1;u <= n;u++){
		int d1 = 0,d2 = 0;//最大值,次大值
		int tmp1 = 0,tmp2 = 0; 
		for(int i = head[u];~i;i = e[i].nex){
			int v = e[i].to;
			if(val[v] > d1) d2 = d1,d1 = val[v];
			else if(val[v] > d2) d2 = val[v];
			tmp1 = (tmp1 + val[v]) % mod;
			tmp2 = (tmp2 + val[v]*val[v]%mod) % mod;
		}
		tmp1 = tmp1 * tmp1 % mod;
		ans = (ans + tmp1 - tmp2 + mod) % mod;
		if(d1*d2 > mx) mx = d1*d2;
	} 
}
int main(){
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i = 1,u,v;i < n;i++){
		scanf("%d%d",&u,&v);
		add_edge(u,v),add_edge(v,u);
	}
	for(int i = 1;i<= n;i++) scanf("%d",val+i);
	solve();
	printf("%d %d\n",mx,ans);
	return 0;
} 

P1144

题意:

给 一 个 无 向 无 权 图 , 计 算 出 从 1 出 发 到 各 点 的 最 短 路 条 数 给一个无向无权图,计算出从1出发到各点的最短路条数 1

思路:

这 题 无 权 值 , 可 以 用 b f s 解 决 , 有 一 题 更 一 般 的 题 目 U E S T C − 1147 这题无权值,可以用bfs解决,有一题更一般的题目UESTC-1147 bfsUESTC1147
传送门
这 题 还 要 处 理 答 案 是 不 是 无 穷 无 尽 的 问 题 这题还要处理答案是不是无穷无尽的问题
首 先 思 考 为 什 么 会 出 现 无 穷 无 尽 的 答 案 , 因 为 边 权 允 许 为 0 首先思考为什么会出现无穷无尽的答案,因为边权允许为0 0
设 想 只 要 有 一 个 在 1 到 n 最 短 路 上 的 点 , 他 有 一 条 边 权 为 0 的 出 边 设想只要有一个在1到n最短路上的点,他有一条边权为0的出边 1n0
那 么 只 要 在 经 过 这 个 点 的 时 候 走 一 次 这 条 边 权 为 0 的 边 是 一 个 方 案 那么只要在经过这个点的时候走一次这条边权为0的边是一个方案 0
走 两 次 , 走 三 次 . . . . . 都 是 一 个 方 案 走两次,走三次.....都是一个方案 .....
因 此 出 现 了 无 穷 无 尽 因此出现了无穷无尽
因 此 只 要 在 更 新 过 程 中 , 不 允 许 出 现 这 种 情 况 , 因此只要在更新过程中,不允许出现这种情况,
一 出 现 就 把 路 径 数 量 设 为 − 1 一出现就把路径数量设为-1 1

P 1144 C o d e P1144Code P1144Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 1e6+10;
const int mod = 1e5+3;
struct Edge{
	int to,nex,w;
}e[N<<1];
int head[N],idx;
int dist[N],ans[N];
bool st[N];
void add_edge(int u,int v,int w){
	e[idx].to = v;
	e[idx].w = w;
	e[idx].nex = head[u];
	head[u] = idx++;
}
int n,m;
priority_queue < pair<int,int> > q;
void Dij(int s){
	dist[s] = 0;
	ans[s] = 1;
	q.push(make_pair(0,s));
	while(q.size()){
		int u = q.top().second;q.pop();
		if(st[u]) continue;
		st[u] = 1;
		for(int i = head[u];~i;i = e[i].nex){
			int v = e[i].to;
			if(dist[v] > dist[u] + e[i].w){
				dist[v] = dist[u] + e[i].w;
				ans[v] = ans[u];
				q.push(make_pair(-dist[v],v));
			}else if(dist[v] == dist[u] + e[i].w){
				ans[v] = (ans[v] + ans[u]) % mod;
			}
			
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	memset(head,-1,sizeof(head));
	memset(dist,inf,sizeof(dist));
	for(int i = 0,u,v;i < m;i++){
		scanf("%d%d",&u,&v);
		add_edge(u,v,1),add_edge(v,u,1);
	}
	Dij(1);
	for(int i = 1;i <= n;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}

U E S T C − 1147 C o d e UESTC-1147Code UESTC1147Code

#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring> 
#include <iostream>
using namespace std;
const int N = 2e3+10;
const int inf = 0x3f3f3f3f;
const int mod = 1000000009;
int head[N],idx;
int dist[N],ans[N];
bool st[N];
struct Edge{
	int to,nex,w;
}e[N<<1];
void add_edge(int u,int v,int w){
	e[idx].to = v;
	e[idx].nex = head[u];
	e[idx].w = w;
	head[u] = idx++;
}
void Dij(int s){
	dist[s] = 0,ans[s] = 1;
	priority_queue< pair<int,int> > q;
	q.push(make_pair(0,s));
	while(q.size()){
		int u = q.top().second;q.pop();
		if(st[u]) continue;
		st[u] = 1;
		for(int i = head[u];~i;i = e[i].nex){
			int v = e[i].to,w = e[i].w;
			if(dist[v] > dist[u] + w){
				dist[v] = dist[u] + w;
				if(w == 0) ans[v] = -1;
				else ans[v] = ans[u];
				q.push(make_pair(-dist[v],v));
			}else if(dist[v] == dist[u] + w){
				if(w == 0){
					ans[v] = -1;
				}else if(ans[u] == -1 || ans[v] == -1){
					ans[v] = -1;
				}else ans[v] = (ans[u] + ans[v]) % mod;
			}
		}
	}
}

int n,m;
int main(){
	memset(head,-1,sizeof(head));
	memset(dist,inf,sizeof(dist));
	scanf("%d%d",&n,&m);
	for(int i = 0,u,v,w;i < m;i++){
		scanf("%d%d%d",&u,&v,&w);
		add_edge(u,v,w),add_edge(v,u,w);
	}
	Dij(1);
	printf("%d\n",ans[n]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值