幸福路径

本文探讨了一种求解有向图中从给定起点出发,沿着边爬行的蚂蚁获得最大幸福度路径的方法。通过定义状态转移方程并进行迭代更新,最终找到H值的最大可能值。

幸福路径

 

【问题描述】

有向图Gn个顶点1, 2, …, n,点i的权值为w(i)。现在有一只蚂蚁,从给定的起点v0出发,沿着图G的边爬行。开始时,它的体力为1。每爬过一条边,它的体力都会下降为原来的ρ倍,其中ρ是一个给定的小于1的正常数。而蚂蚁爬到某个顶点时的幸福度,是它当时的体力与该点权值的乘积。 我们把蚂蚁在爬行路径上幸福度的总和记为H。很显然,对于不同的爬行路径,H的值也可能不同。小Z对H值的最大可能值很感兴趣,你能帮助他计算吗?注意,蚂蚁爬行的路径长度可能是无穷的。

【输入格式】

输入文件为path.in。文件的每一行中两个数之间用一个空格隔开。 输入文件第一行包含两个正整数n, m,分别表示G中顶点的个数和边的条数。 第二行包含n个非负实数,依次表示n个顶点权值w(1), w(2), …, w(n)。 第三行包含一个正整数v0,表示给定的起点。 第四行包含一个实数ρ,表示给定的小于1的正常数。 接下来m行,每行两个正整数x, y,表示<x, y>是G的一条有向边。可能有自环,但不会有重边。

【输出格式】

输出文件path.out仅包含一个实数,即H值的最大可能值,四舍五入到小数点后一位。

【样例输入】

5 5 10.0 8.0 8.0 8.0 15.0 1

0.5

1 2

2 3

3 4

4 2

4 5

【样例输出】

18.0

 

【样例说明】

当蚂蚁的爬行路径为1→2→3→4→2→3→4→…→2→3→4→…时,H = 10.0 +8.0*0.5+8.0*0.5^2+⋯。可以证明,这个无穷序列的总和为18.0,且这就是H的最大值。 另外,若本样例中w(5)改为17.0,其余数据不变,则当路径为1→2→3→4→5时,H = 18.0625。可以证明,这就是此时H的最大值。

【数据规模】

对于20%的数据,ρ ≤0.5; 另有20%的数据,保证H的最大值在有限路径上取到; 对于100%的数据,n ≤ 100,m ≤ 1000,ρ ≤ 1 – 10^-6,w(i) ≤ 100 (i = 1, 2, …, n)。

 

 

令f[i][j][t]为从点i走到点j花2^t步的最大幸福值,(不包括一开始站在i点的那个幸福值,这样不会在i->k->j中k转移处算重复,但是注意最后结果要加上begin点的幸福值)

那么有f[i][j][t]=max{f[i][k][t-1]+f[k][j][t-1]*p^(2^(t-1))},t要稍微大一点,最起码有25吧

迭代多次即可得到答案的近似值,用到的其实是倍增的思想。

注意蚂蚁可能卡死在某个点不动,因此初始要将邻接矩阵清为-INF,然后每个点连一条边为0的自环此外注意下卡死时最后经过的那个点的权值会不会被统计, 这里可能会挂。至于自环问题,一开始没想明白,加上就A了,否则WA一个点,不过后来想了想,我觉得可能是因为这个1->2->3->4线性图,找2^2时不存在4条路啊,只能是其中一个原地不动,凑够3条,所以原数组的定义可以修改为f[i][j][t]为从点i走到点j花不超过2^t步的最大幸福值。

 1 #define ce freopen("path.in","r",stdin);
 2 #define na freopen("path.ans","w",stdout);
 3 
 4 #include<cstdio>
 5 #include<cstring>
 6 using namespace std;
 7 
 8 int n,m,v,x,y;
 9 double p,d[105];
10 double f[105][105][50];
11 double ans;
12 int main(){
13     memset(f,-5,sizeof(f));
14     scanf("%d%d",&n,&m);
15     for(int i=1;i<=n;i++){
16         scanf("%lf",&d[i]);
17         f[i][i][0]=0;
18     }
19     scanf("%d%lf",&v,&p);
20     for(int i=1;i<=m;i++){
21         scanf("%d%d",&x,&y);
22         f[x][y][0]=d[y]*p;
23     }
24     for(int q=1;q<=35;q++){
25         for(int k=1;k<=n;k++){
26             for(int i=1;i<=n;i++){
27                 for(int j=1;j<=n;j++){
28                     if(f[i][j][q]<f[i][k][q-1]+f[k][j][q-1]*p)
29                         f[i][j][q]=f[i][k][q-1]+f[k][j][q-1]*p;
30                     if(i==v){
31                         if(f[i][j][q]>ans) ans=f[i][j][q];
32                     }
33                 }
34             }
35         }
36         p*=p;
37     }
38     printf("%.1lf",ans+d[v]);
39     getchar(); getchar(); getchar();
40 }
View Code

 

posted @ 2015-10-09 20:17 Lenicodes 阅读( ...) 评论( ...) 编辑 收藏
帮我根据这个思路调试代码考虑到体力是按指数级衰减的,所以我们只要做下面的一个dp就可以解决问题了。 令f[t][i][j]表示走2t步,从i走到j获得的最大幸福度。 f[t][i][j] = max(f[t - 1][i][k] + f[t - 1][k][j] * p2t - 1) 当t足够大时,f得到的就近似为最大幸福度,或者说当p < eps 时,得到的就近似为最大幸福度。 题面: # P4308 [CTSC2011] 幸福路 ## 题目描述 有向图 $G$ 有 $n$ 个顶点 $1, 2, \cdots, n$,点 $i$ 的权值为 $w(i)$。 现在有一只蚂蚁,从给定的起点 $v_0$ 出发,沿着图 $G$ 的边爬行。开始时,它的体力为 $1$。每爬过一条边,它的体力都会下降为原来的 $\rho$ 倍,其中 $\rho$ 是一个给定的小于 $1$ 的正常数。而蚂蚁爬到某个顶点时的幸福度,是它当时的体力与该点权值的乘积。 我们把蚂蚁在爬行路径幸福度的总和记为 $H$。很显然,对于不同的爬行路径,$H$ 的值也可能不同。小 Z 对 $H$ 值的最大可能值很感兴趣,你能帮助他计算吗?注意,蚂蚁爬行的路径长度可能是无穷的。 ## 输入格式 每一行中两个数之间用一个空格隔开。 输入文件第一行包含两个正整数 $n,m$,分别表示 $G$ 中顶点的个数和边的条数。 第二行包含 $n$ 个非负实数,依次表示 $n$ 个顶点权值 $w(1), w(2), \cdots, w(n)$。 第三行包含一个正整数 $v_0$,表示给定的起点。 第四行包含一个实数 $\rho$,表示给定的小于 $1$ 的正常数。 接下来 $m$ 行,每行两个正整数 $x, y$,表示 $(x, y)$ 是 $G$ 的一条有向边。可能有自环,但不会有重边。 ## 输出格式 仅包含一个实数,即 $H$ 值的最大可能值,四舍五入到小数点后一位。 ## 输入输出样例 #1 ### 输入 #1 ``` 5 5 10.0 8.0 8.0 8.0 15.0 1 0.5 1 2 2 3 3 4 4 2 4 5 ``` ### 输出 #1 ``` 18.0 ``` ## 说明/提示 对于 $100\%$ 的数据,$1\leq n \le 100$,$1\leq m \le 1000$,$0 < \rho \le 1 - 10^{-6}$,$0\leq w(i) \leq 100$。 代码: #include<bits/stdc++.h> #define itn int using namespace std; const int MAXN = 105; const int inf = 0x3f3f3f3f; itn N, M; double w[MAXN], p, dp[MAXN][MAXN][40], ans; itn st; void init(){ for( int i = 1; i < MAXN; i ++ ){ for( itn j = 1; j < MAXN; j ++ ){ for( itn k = 1; k < 40; k ++ ){ if( i == j ) continue; dp[i][j][k] = -inf; } } } for( itn i = 1; i <= M; i ++ ){ itn a, b; cin >> a >> b; dp[a][b][0] = w[b] * p; } } int main(){ cin >> N >> M; for( itn i = 1; i <= N; i ++ ){ cin >> w[i]; } cin >> st >> p; init(); for( itn t = 1; t < 30; t ++ ) for( int k = 1; k <= N; k ++ ) for( itn i = 1; i <= N; i ++ ) for( itn j = 1; j <= N; j ++ ){ dp[i][j][t] = max( dp[i][j][t], dp[i][k][t] + dp[k][j][t] * p ); if( i == st ) ans = max( ans, dp[i][j][t] ); } cout << ans; return 0; }
10-02
帮我调试和完善下面代码,不要改变变量名和算法: #include<bits/stdc++.h> #define itn int using namespace std; const int MAXN = 105; const int inf = 0x3f3f3f3f; itn N, M; double w[MAXN], p, dp[MAXN][MAXN][40], ans; itn st; void init(){ for( int i = 1; i < MAXN; i ++ ){ for( itn j = 1; j < MAXN; j ++ ){ for( itn k = 1; k < 40; k ++ ){ if( i == j ) continue; dp[i][j][k] = -inf; } } } for( itn i = 1; i <= M; i ++ ){ itn a, b; cin >> a >> b; dp[a][b][0] = w[b] * p; } } int main(){ cin >> N >> M; for( itn i = 1; i <= N; i ++ ){ cin >> w[i]; } cin >> st >> p; init(); for( itn t = 1; t < 30; t ++ ) for( int k = 1; k <= N; k ++ ) for( itn i = 1; i <= N; i ++ ) for( itn j = 1; j <= N; j ++ ){ dp[i][j][t] = max( dp[i][j][t], dp[i][k][t] + dp[k][j][t] * p ); if( i == st ) ans = max( ans, dp[i][j][t] ); } cout << ans; return 0; } 题面:# P4308 [CTSC2011] 幸福路 题目描述 有向图 G G 有 n n 个顶点 1 , 2 , ⋯   , n 1,2,⋯,n,点 i i 的权值为 w ( i ) w(i)。 现在有一只蚂蚁,从给定的起点 v 0 v 0 ​ 出发,沿着图 G G 的边爬行。开始时,它的体力为 1 1。每爬过一条边,它的体力都会下降为原来的 ρ ρ 倍,其中 ρ ρ 是一个给定的小于 1 1 的正常数。而蚂蚁爬到某个顶点时的幸福度,是它当时的体力与该点权值的乘积。 我们把蚂蚁在爬行路径幸福度的总和记为 H H。很显然,对于不同的爬行路径, H H 的值也可能不同。小 Z 对 H H 值的最大可能值很感兴趣,你能帮助他计算吗?注意,蚂蚁爬行的路径长度可能是无穷的。 输入格式 每一行中两个数之间用一个空格隔开。 输入文件第一行包含两个正整数 n , m n,m,分别表示 G G 中顶点的个数和边的条数。 第二行包含 n n 个非负实数,依次表示 n n 个顶点权值 w ( 1 ) , w ( 2 ) , ⋯   , w ( n ) w(1),w(2),⋯,w(n)。 第三行包含一个正整数 v 0 v 0 ​ ,表示给定的起点。 第四行包含一个实数 ρ ρ,表示给定的小于 1 1 的正常数。 接下来 m m 行,每行两个正整数 x , y x,y,表示 ( x , y ) (x,y) 是 G G 的一条有向边。可能有自环,但不会有重边。 输出格式 仅包含一个实数,即 H H 值的最大可能值,四舍五入到小数点后一位。 输入输出样例 #1 输入 #1 5 5 10.0 8.0 8.0 8.0 15.0 1 0.5 1 2 2 3 3 4 4 2 4 5 输出 #1 18.0 说明/提示 对于 100 % 100% 的数据, 1 ≤ n ≤ 100 1≤n≤100, 1 ≤ m ≤ 1000 1≤m≤1000, 0 < ρ ≤ 1 − 10 − 6 0<ρ≤1−10 −6 , 0 ≤ w ( i ) ≤ 100 0≤w(i)≤100。
10-02
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值