2019.03.30【NOIP提高组】模拟 B 组 与机关的决战

本文介绍了解决JZOJ4215问题的一种动态规划方法,该问题涉及在一个特殊的无向图中追踪并捕获目标。通过构建最短路径树并利用动态规划求解最优策略,实现最大化捕获概率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JZOJ4215. 与机关的决战

Description

Lab与机关SERN展开了最后决战。
SERN派出了凶恶的FB,而Labmem们要捕获FB。
FB刚刚在第三水车厂露过行踪,Lab首领冈伦决定倾Lab之力全力追捕FB。
抓捕发生的地点可以表示成一张无向带权图,第三水车厂位于节点1。
冈伦仔细研究了FB的行为模式后得出以下结论:
首先,FB拥有极强的反跟踪能力,因此他深知不走回头路的重要性。他永远不会访问任何一个节点两次。
其次,FB行动以“速”著称,所以FB总是走最短路。亦即,FB访问任何一个节点时,走的路线都是从第三水车厂到该节点的最短路。这里保证从第三水车厂(节点1)到任意节点的最短路唯一。
第三,FB处于不停运动之中。亦即,只要有相邻的节点能满足前两条,他必然会移动。若有多个相邻节点可供选择,他会随机等概率选择一个作为他的移动目标。若没有节点满足这一要求,那么FB会跳世界线。而一旦FB跳世界线,Lab的这次行动很显然就意味着彻底失败。
冈伦分析出以上结论后决定,只能在节点上布置Labmem,实施埋伏抓捕。但是,FB的身体素质、格斗技术都十分优秀。因此,即使FB中伏,也有一定概率逃脱。当然,随着在此地埋伏的Labmem的数目的增多,逃脱几率会减小。如果逃脱成功,FB会像什么都没发生一样,继续按上文所述的原则行动。
注意,FB一旦到达某个节点,埋伏在该处的Labmem会立即行动,只有FB逃脱了当前节点的抓捕后才能进行下一步行动(继续移动或跳世界线),包括节点1,也就是说FB需要先逃脱节点1的埋伏才能进行他的第一次行动。
现在冈伦已经知道各节点设置不同数量的Labmem能成功抓捕FB的概率,现在冈伦想要使得抓捕成功的概率最大。

Input

输入文件第一行包含两个数N,M,分别表示节点数和边数。
接下来M行,每行3个数u,v,w,表示节点u和v之间有一条权值为w的无向边。
接下来一个数S,表示可以参与埋伏的Labmem成员总数。
接下来N行,每行S个数,第i行第j个数Pij表示在节点i埋伏j个Labmem抓捕成功的概率。注意,如果不埋伏任何Labmem,那么显然绝不可能捕获FB。

Output

输出文件仅包含一个实数,保留4位小数,表示最大捕获概率。

Sample Input

4 4
1 2 1
1 3 2
2 4 3
3 4 1
2
0.01 0.1
0.5 0.8
0.5 0.8
0.7 0.9

Sample Output

0.6000

Data Constraint

对于20%的数据,N,S<=6
对于50%的数据,N,S<=30,每个节点度数不大于3
对于100%的数据,N,S<=200,M<=20000,1<=a,b<=N,1<=c<=10000,
0<Pij<=1
无自环、无重边

Solution

一道有点深的DP
首先要看懂题目
说白了就是走出一棵树
先用最短路构建出这棵树
然后显然想到用DP
设f[i][j]为以i为根的子树用了j个人的最大捕获概率
转移方程显然

不选:f[i][j]=max{f[i][j-k]+(f[s][k]/son)}(当前儿子为s,子树里用了k个人,共有sum个儿子)
选:f[i][j]=max{p[i][l]+f[i][j-l]*(1-p[i][l])}(这个点放了l个人)
注意j要倒着枚举避免后效性
两个方程分开做(不然很麻烦)

AC

Code

#include<bits/stdc++.h>
#define open_in(x) freopen(""#x".in","r",stdin);
#define open_out(x) freopen(""#x".out","w",stdout);
#define open_(x) freopen(""#x".in","w",stdout);
#define open(x) open_in(x);open_out(x);
#define mes(x,y) memset(x,y,sizeof(x));
#define mec(x,y) memcpy(x,y,sizeof(x));
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int X=210;
const int M=1e9+7;

inline void read(int &x)
{
	int f=1;char ch=getchar();x=0;
	while (ch<'0' || ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	x*=f;
}

struct edge
{
	int x,v,next;
}e[X*X];
int last[X],k=0;
void add(int x,int y,int z){e[++k]=(edge){y,z,last[x]};last[x]=k;}

int n,m,S;
db p[X][X];

int Q[X*X],l,r,dis[X],bz[X];

db f[X][X];
void dfs(int x)
{
	int sum=0;
	for (int i=last[x];i;i=e[i].next)
		sum+=(dis[x]+e[i].v==dis[e[i].x]);
	for (int i=last[x];i;i=e[i].next)
	{
		int s=e[i].x,v=e[i].v;
		if (dis[x]+v==dis[s])
		{
			dfs(s);
			for (int j=S;j>=1;j--)
				for (int k=1;k<=j;k++)
					f[x][j]=max(f[x][j],f[x][j-k]+(f[s][k]/sum));
		}
	}
	for (int j=S;j>=1;j--)
		for (int l=1;l<=j;l++)
			f[x][j]=max(f[x][j],p[x][l]+f[x][j-l]*(1-p[x][l]));
}

int main()
{
	open_in(Labmem);
	int i,j;
	read(n),read(m);
	for (i=1;i<=m;i++)
	{
		int x,y,z;
		read(x),read(y),read(z);
		add(x,y,z),add(y,x,z);
	}
	read(S);
	for (i=1;i<=n;i++)
		for (j=1;j<=S;j++)
			scanf("%lf",&p[i][j]);

	mes(dis,127);
	dis[1]=0;
	l=0,Q[r=1]=1;
	while (l++<r)
	{
		int x=Q[l];
		bz[x]=0;
		for (i=last[x];i;i=e[i].next)
		{
			int s=e[i].x,v=e[i].v;
			if (dis[x]+v<dis[s])
			{
				dis[s]=dis[x]+v;
				if (!bz[s])
				{
					bz[s]=1;
					Q[++r]=s;
				}
			}
		}
	}
	dfs(1);
	printf("%.4lf\n",f[1][S]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值