题解——未找到的路

题目背景

有的路,美好的外表下,是危机四伏的阴影。

有的路,走起来很艰难,在剧烈的心痛中,明知路的尽头是绝望却无法回头。

有的路,布满阴霾,伴随着黑暗和孤独,看不清方向。

在路上,我们一边走,一边流泪。

在路上,我们一边走,一边失去。

题目描述

“你还记得那个地方吗?”

面前的你,闭上眼睛,喃喃着。

“当然,那怎么会忘却呢。$n$ 个记忆碎片的多重空间,每个碎片都有单向道路通往其他碎片。多美的地方”

“那条路呢?从 $S$ 到 $T$ 的路,那条最美好的路呢?你还记得吗?” 你突然看向我。

一阵风吹过,沉默。

我大约知道的,一条路径的美好度是它的长度和通过它的时间的比值。

我确实忘记了,那条路,那条未找到的路,究竟在何方。

过去的过去,还是过去,未来的未来,还是未来么?

请你帮我一次,就一次,好吗。

可是,在这条路上,我能不能遇见你,而你,会不会陪我走完这条路呢?

输入格式

第一行有三个正整数 $n$,$S$,$T$ 如题意。

接下来有两个 $n \times n$ 的矩阵。

其中第一个矩阵的第 $i$ 行第 $j$ 个数表示从碎片 $i$ 到碎片 $j$ 道路的长度 $s$。

其中第二个矩阵的第 $i$ 行第 $j$ 个数表示从碎片 $i$ 到碎片 $j$ 通过的时间 $t$ 。

保证两个矩阵中第 $i$ 行第 $i$ 个数为 $0$,其余为正整数。

输出格式

一行一个实数 $k$ 表示最大的美好度,保留三位小数。

数据范围

对于 $10\% $ 的数据 $n\le 5$。

对于另外 $30\% $ 的数据 $s_{i,j}$ 均相等且 $t_{i,j}$ 均相等(不包括 $i=j$ 的情况)。

对于另外 $20\% $ 的数据,保证最后输出的答案等于一个整数。

对于 $100\% $ 的数据 $n\le 200$,  $1\le s_{i,j},t_{i,j}\le 10^5$。

Solution:

(不得不说出题人文采很不错)

对于 $10\%$ 的数据,枚举每次走的边,暴搜

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #define R register
 5 #define MAXN 210
 6 #define CMAX(_a,_b) ((_a<_b)&&(_a=_b))
 7 using namespace std;
 8 inline int read()
 9 {
10     int _a = 0;
11     char ch = getchar();
12     while(ch>'9'||ch<'0') ch = getchar();
13     while(ch>='0'&&ch<='9') _a = _a * 10 + ch - '0', ch = getchar();
14     return _a;
15 }
16 int W[MAXN][MAXN],C[MAXN][MAXN],S,T,n;
17 int vis[MAXN];
18 double Ans = 0;
19 void DFS(int u,int w,int c)
20 {
21     vis[u]++;
22     if(vis[u]>2) {vis[u]--;return;}
23     if(u == T) CMAX(Ans,(double)w/c);
24     for(R int i = 1; i <= n; i++)
25     {
26         if(i == u) continue;
27         DFS(i,w+W[u][i],c+C[u][i]);
28     }
29     vis[u]--;
30 }
31 int main()
32 {
33     memset(vis,0,sizeof(vis));
34     n = read(), S = read(), T = read();
35     for(R int i = 1; i <= n; i++)
36         for(R int j = 1; j <= n; j++) W[i][j] = read();
37     for(R int i = 1; i <= n; i++)
38         for(R int j = 1; j <= n; j++) C[i][j] = read();    
39     DFS(S,0,0);
40     printf("%.3lf",Ans);
41     return 0;
42 }

对于 $30\%$ 的数据,输出任意一条边上的 $W/C$

然后讲讲正解吧

考虑过很多算法($DP$,贪心等),发现似乎很难直接从原图得出答案,考虑二分答案(本来想到过,但是因为不会写小数的二分就。。。)

假如答案为 $Ans$,有 $\sum W - \sum C * Ans = 0$,分解成每条边的权值,即 $W[i] - Ans * C[i]$。

可以使用 $Floyd$ 算出最长路,而且使用该算法比较容易判正环(如出现正环则 $Ans$ 比行),当然 $SPFA$ 亦可

启发:像这种感觉很难直接从原图中获得答案的时候考虑构造答案然后 $check$,而最常用的方式是二分(考场上本来想到的,可是由于是小数然后就。。)

          考虑答案与题目的关系,最常见的是构造新图(形象意义上,同P3287(骑行川藏)),通过建模的方式进行 $check$ ,使用图论算法(最短/长路,SCC,BCC,MST)

(似乎图上很少用 DP 的)

AC代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define R register
 4 #define MAXN 210
 5 #define CMAX(_a,_b) ((_a<_b)&&(_a=_b))
 6 const double EPS = 1e-5;
 7 using namespace std;
 8 inline int read(){
 9     R int _a = 0,_b = 1;
10     char ch = getchar();
11     while(ch>'9'||ch<'0') {if(ch=='-')_b=-1;ch=getchar();}
12     while(ch>='0'&&ch<='9') _a=_a*10+ch-'0',ch=getchar();
13     return _a*_b; 
14 }
15 int n,S,T;
16 int s[MAXN][MAXN],t[MAXN][MAXN];
17 double Dis[MAXN][MAXN];
18 inline bool Check(double M)
19 { 
20     for(R int i = 1; i <= n; i++)
21         for(R int j = 1; j <= n; j++)
22         Dis[i][j] = (double)s[i][j] - M * t[i][j];
23     for(R int k = 1; k <= n; k++)
24         for(R int i = 1; i <= n; i++)
25             for(R int j = 1; j <= n; j++)
26                 CMAX(Dis[i][j],Dis[i][k]+Dis[k][j]);
27     for(R int i = 1; i <= n; i++) if(Dis[i][i] > 0) return 1;
28     return Dis[S][T] > 0;
29 }
30 int main()
31 {
32     n = read(), S = read(), T = read();
33     for(R int i = 1; i <= n; i++)
34         for(R int j = 1; j <= n; j++) s[i][j] = read();
35     for(R int i = 1; i <= n; i++)
36         for(R int j = 1; j <= n; j++) t[i][j] = read();    
37     double L = 0,RR = 100000,Mid;
38     while(RR - L > EPS){
39         Mid = (L + RR) / 2.0;
40         if(Check(Mid)) L = Mid;
41         else RR = Mid;
42     }
43     printf("%.3lf",L);
44     return 0;
45 }

猜你喜欢

转载自www.cnblogs.com/QuickSilverX/p/10887402.html