题干:
链接:https://ac.nowcoder.com/acm/contest/370/H
来源:牛客网
Rinne 学到了一个新的奇妙的东西叫做动态图,这里的动态图的定义是边权可以随着操作而变动的图。
当我们在这个图上经过一条边的时候,这个图上所有边的边权都会发生变动。
定义变动函数 f(x)=11−xf(x)=11−x,表示我们在图上走过一条边后,图的边权变动情况。
这里指的“图的变动”的意思是将每条边的边权代入上函数,得到的值即为该次变动后的边权。
现在 Rinne 想要知道,在这个变动的图上从 1 到 n 的最短路径。
因为 Rinne 不喜欢负数,所以她只需要你输出经过的边权权值绝对值之和最小的那个值就可以了。
输出答案保留三位小数。
输入描述:
第一行两个正整数 N,M,表示这个动态图的点数和边数。
接下来 M 行,每行三个正整数 u,v,w,表示存在一条连接点 u,v 的无向边,且初始权值为 w。
输出描述:
如果能到达的话,输出边权绝对值之和最小的答案,保留三位小数。
否则请输出 -1。
示例1
输入
复制
3 3
1 2 2
2 3 2
3 1 3
输出
复制
3.000
说明
走 1→2→31→2→3,总花费 2+|11−2|=32+|11−2|=3
备注:
n≤100000,m≤300000,2≤x≤1000
解题报告:
mmp的这题卡常卡我半天,,不能用spfa不能用vector、、、还有就是注意数组大小,边数要开6*MAXM,,因为是双向边、、不然就会TLE。
读题发现这个函数是个迭代函数。
发现这一点之后这题就解决一大半了,建三层的图,每一层只能连向更高层。跑Dijkstra就行了。最后在三个答案中取最小。当然这题也可以用dp的思想去做。
注意这题有个坑,就是你加边的时候不能直接更新了w,就将w取绝对值了,而是应该,传参的时候传进去绝对值,但是w还是保持本身的值。(这个坑不注意还真发现不了、、、就看你写的姿势如何了)
AC代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
typedef pair<int,double> PID;
typedef pair<double,int> PDI;
const double INF = 99999999999;
int n,m;
//vector<PID > vv[3*MAX];
double dis[MAX*3];
bool vis[MAX*3];
int head[MAX*3];
struct Node {int to,ne;double w;
} e[MAX*15];
int tot;
void add(int u,int v,double w) {e[++tot].to = v;e[tot].w = w;e[tot].ne = head[u];head[u] = tot;
}
void Dijkstra() {for(int i = 1; i<=3*n; i++) dis[i] = INF;dis[1] = 0;priority_queue<PDI> pq;pq.push(pm(0,1));while(!pq.empty()) {PDI cur = pq.top();pq.pop();if(vis[cur.se]) continue;vis[cur.se] = 1;for(int i = head[cur.se]; i!=-1; i=e[i].ne) {int v = e[i].to;if(dis[cur.se] + e[i].w < dis[v]) {dis[v] = dis[cur.se] + e[i].w;pq.push(pm(-dis[v],v));}}}
}
int main()
{memset(head,-1,sizeof head);cin>>n>>m;int u,v;double w;for(int i = 1; i<=m; i++) {scanf("%d%d%lf",&u,&v,&w);//w = fabs(w);add(u,v+n,fabs(w));add(v,u+n,fabs(w));w = (1.0/(1-w));add(u+n,v+2*n,fabs(w));add(v+n,u+2*n,fabs(w));w = (1.0/(1-w));add(u+2*n,v,fabs(w));add(v+2*n,u,fabs(w));}Dijkstra();double ans = INF;for(int i = 0; i<=2; i++) {ans = min(ans,dis[n+i*n]);}if(ans > 9999999999) printf("-1\n");else printf("%.3lf\n",ans);return 0 ;}
AC代码2:(dp思路)
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAX=5e5+10;
typedef long long ll;
struct lenka
{int x,y;double cost;bool operator<(const lenka&q)const{return q.cost<cost;}
};
vector<pair<int,int> >e[MAX];
priority_queue<lenka>p;
double d[MAX][3];
double f(int x,int tp)
{if(tp==0)return x*1.0;if(tp==1)return 1/(x-1.0);return 1-1.0/x;
}
void bfs()
{p.push((lenka){1,0,0.0});d[1][0]=0;while(!p.empty()){lenka now=p.top();p.pop();if(fabs(d[now.x][now.y]-now.cost)>1e-8)continue;for(int i=0;i<e[now.x].size();i++){pair<int,int>nex=e[now.x][i];if(d[nex.first][(now.y+1)%3]>now.cost+f(nex.second,now.y)){d[nex.first][(now.y+1)%3]=now.cost+f(nex.second,now.y);p.push((lenka){nex.first,(now.y+1)%3,d[nex.first][(now.y+1)%3]});}}}
}
int main()
{int n,m;cin>>n>>m;for(int i=1;i<=n;i++)d[i][0]=d[i][1]=d[i][2]=6e18;while(m--){int x,y,z;scanf("%d%d%d",&x,&y,&z);e[x].push_back({y,z});e[y].push_back({x,z});}bfs();double ans=*min_element(d[n],d[n]+3);if(ans>5e18)puts("-1");else printf("%.3lf\n",ans);return 0;
}
再来一个:
#pragma comment(linker, "/STACK:102400000,102400000")//防止栈溢出!!!
#include<cstdio>
#include<iostream>
#include<stdlib.h>
#include<malloc.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include<time.h>
#include<ctype.h>
#define REP(I,N) for (I=0;I<N;I++)
#define rREP(I,N) for (I=N-1;I>=0;I--)
#define rep(I,S,N) for (I=S;I<N;I++)
#define rrep(I,S,N) for (I=N-1;I>=S;I--)
#define For(i,x,y) for(long long i=(long long)(x);i<(long long)(y);++i)
#define rFOR(I,S,N) for(long long I=N;I>=S;I--)
#define SWAP(x,y) {(x) = (x)^(y); (y)=(x)^(y); (x)=(x)^(y);}
#define D(x) cerr<<#x<<" = "<<x<<endl
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double eps=1e-6;
const int MOD = 1e9+6 ;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const int MAXN =100010;const double pi=acos(-1.0);
template<class T>inline void rd(T &x){int Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T> inline T Maxx(const T&a,const T&b){return a < b?b:a;}
template<class T> inline T Minn(const T&a,const T&b){return a < b?a:b;}
template<class T> inline T gcd(T a,T b) {return b?gcd(b,a%b):a;}
template<class T> inline T lcm(T x,T y) {return x/gcd(x,y)*y;}
struct node{ int to;int cnt;ll w;
// bool operator <(const node &t)const{
// if(le-ri==t.le-t.ri)
// return le<t.le;
// else
// return le-ri<t.le-t.ri;
// }
};
struct qnode{int v;//节点idint k;double c;//源节点到v的暂且最短距离 bool operator <(const qnode &r)const{return c>r.c;}
};
struct Edge
{int to , next;double w ;
} e[600010];//bool vis[MAXN][3];
double dist[MAXN][3];
//点的编号从 1 开始int cnt,p[ MAXN ] ;
void Add_Edge(const int x ,const int y ,const int w ){e[ ++cnt ] . to = y ;e[ cnt ] . next = p[ x ];e[ cnt ] . w = w ;p[ x ] = cnt ;return ;
}
inline double f( double X , int N ) {switch( N ) {case 1 : {return X ;break ;}case 2 : {return fabs( 1 / ( 1 - X ) ) ;break ;}case 0 : {return fabs( ( X - 1 ) / X ) ;break ;}}return 0.0 ;
}
double dinf;
void Dijkstra(int start ){//memset(vis,false,sizeof(vis));priority_queue<qnode>que;memset(dist,127,sizeof(dist));while(!que.empty())que.pop();dinf=dist[0][0];dist[start][0]=0;que.push((qnode){start,0,0});while( que.size()){qnode tmp=que.top();que.pop();int u=tmp.v;if(fabs(tmp.c-dist[u][tmp.k])>1e-7)continue;//如果更新过此状态,就不再放入队列// vis[u]=true;int nk=(tmp.k+1)%3;for( int i=p[u] ; i ; i = e[ i ].next ){int v=e[ i ].to;double cost=tmp.c+ f(e[ i ].w,nk) ;if( dist[v][nk]> cost){dist[v][nk]=cost;que.push( (qnode){v,nk,cost});}}}
}
int main(int argc, char** argv)
{ //freopen("E://dev//input.in","r",stdin); int n,m;scanf("%d%d",&n,&m );int u,v,w ;for(int i=0;i<m;i++){rd(u),rd(v),rd(w);Add_Edge(u,v,w*1.0);Add_Edge(v,u,w*1.0); } Dijkstra(1) ;double ans=Minn(dist[n][0],Minn(dist[n][1],dist[n][2]));if(fabs(ans-dinf)>1e-7)//与初始化的浮点型INF比较大小printf("%.3lf\n",ans);elseputs( "-1" ) ;return 0;
}
或者把这里改了:
inline double f( double X , int N ) {switch( N ) {case 0 : {return X ;break ;}case 1 : {return fabs( 1 / ( 1 - X ) ) ;break ;}case 2 : {return fabs( ( X - 1 ) / X ) ;break ;}}return 0.0 ;
}
double dinf;
void Dijkstra(int start ){//memset(vis,false,sizeof(vis));priority_queue<qnode>que;memset(dist,127,sizeof(dist));while(!que.empty())que.pop();dinf=dist[0][0];dist[start][2]=0;que.push((qnode){start,2,0});
之所以最后三个的代码(换句话说是倒数第三个 和 后两个)之所以有区别(指的是f函数的区别),就是因为传入的参数
if(d[nex.first][(now.y+1)%3]>now.cost+f(nex.second,now.y)) 倒数第三个 的代码是now.y+1%3 但是算f函数是now.y。。。
倒数第二个,算f函数也是传的nk(相当于是now.y+1),所以函数体内当然不一样咯。。。
所以其实实质都是一样的,,只不过实现方式不一样而已、、
还有就是while里面的 if(fabs(tmp.c-dist[u][tmp.k])>1e-7)continue;其实可以不用写、、、只是会稍微慢一点点。