洛谷 P4300 BZOJ 1266 [AHOI2006]上学路线route
题目描述
可可和卡卡家住合肥市的东郊,每天上学他们都要转车多次才能到达市区西端的学校。直到有一天他们两人参加了学校的信息学奥林匹克竞赛小组才发现每天上学的乘车路线不一定是最优的。 可可:“很可能我们在上学的路途上浪费了大量的时间,让我们写一个程序来计算上学需要的最少时间吧!” 合肥市一共设有N个公交车站,不妨将它们编号为1…N的自然数,并认为可可和卡卡家住在1号汽车站附近,而他们学校在N号汽车站。市内有M条直达汽车路线,执行第i条路线的公交车往返于站点pi和qi之间,从起点到终点需要花费的时间为ti。(1<=i<=M, 1<=pi, qi<=N) 两个人坐在电脑前,根据上面的信息很快就编程算出了最优的乘车方案。然而可可忽然有了一个鬼点子,他想趁卡卡不备,在卡卡的输入数据中删去一些路线,从而让卡卡的程序得出的答案大于实际的最短时间。而对于每一条路线i事实上都有一个代价ci:删去路线的ci越大卡卡就越容易发现这个玩笑,可可想知道什么样的删除方案可以达到他的目的而让被删除的公交车路线ci之和最小。 [任务] 编写一个程序: 从输入文件中读取合肥市公交路线的信息; 计算出实际上可可和卡卡上学需要花费的最少时间; 帮助可可设计一个方案,删除输入信息中的一些公交路线,使得删除后从家到学校需要的最少时间变大,而被删除路线的ci和最小;向输出文件输出答案。
输入
输入文件中第一行有两个正整数N和M,分别表示合肥市公交车站和公交汽车路线的个数。以下M行,每行(第i行,总第(i+1)行)用四个正整数描述第i条路线:pi, qi, ti, ci;具体含义见上文描述。
输出
输出文件最多有两行。 第一行中仅有一个整数,表示从可可和卡卡家到学校需要的最短时间。 第二行输出一个整数C,表示Ci之和
样例输入
6 7
1 2 1 3
2 6 1 5
1 3 1 1
3 4 1 1
4 6 1 1
5 6 1 2
1 5 1 4
1 2 1 3
2 6 1 5
1 3 1 1
3 4 1 1
4 6 1 1
5 6 1 2
1 5 1 4
样例输出
2
5
5
提示
2<=N<=500, 1<=M<=124 750, 1<=ti, ci<=10 000
合肥市的公交网络十分发达,你可以认为任意两个车站间都可以通过直达或转车互相到达,当然如果在你提供的删除方案中,家和学校无法互相到达,那么则认为上学需要的最短为正无穷大:这显然是一个合法的方案。
吐槽
这题各种单向边双向边变来变去,一会单向,一会双向,一会又单向添加两次,各种乱七八糟的,把我自己绕晕了。只是粗浅地搞懂原理,却没真正理解题目意思和解法真的不好啊!
解题思路
第一问单源最短路,才500个点,随便乱搞套各种模板的节奏,这里我选择了代码量极短的Floyd(后面求最小割即最大流时加边比spfa更方便)。
然后第二问,要把所有最短路径都切断,那么我们用Floyd或其他spfa什么的跑出所有最短路构成的图,每条边的切割代价为c,那么求一波这个图的最小割,就是答案了(好像BZOJ 1002 狼抓兔子啊)。由于最小割等于最大流,所以我们在最短路图上跑最大流算法即可,这里我用的Dinic,蒟蒻版几乎无优化。
源代码
#include<queue> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m; struct Edge_road{ int u,v,w,c; }g[130000]; int dis[510][510],num=1; inline void addg(int u,int v,int w,int c) { g[num++]={u,v,w,c};//不排序的前向星,只加单向边 dis[u][v]=dis[v][u]=min(dis[u][v],w);//dis数组用来跑floyd,加双向边,有重边则选最小边长的一条边 } inline void floyd() { for(int i=1;i<=n;i++) dis[i][i]=0; for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); } struct Edge_flow{ int next,to,flow; }e[300010]; int cnt=2,head[510]={0}; inline void add(int u,int v,int c) { e[cnt]={head[u],v,c}; head[u]=cnt++; e[cnt]={head[v],u,0}; head[v]=cnt++; } int dep[510]={0}; bool bfs() { memset(dep,0,sizeof(dep)); queue<int> q; dep[1]=1; q.push(1); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(!dep[v]&&e[i].flow) { dep[v]=dep[u]+1; q.push(v); } } } return dep[n]!=0; } int dfs(int u,int f) { if(u==n||f==0) return f; int sum=0; for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(dep[v]==dep[u]+1&&e[i].flow) { int delta=dfs(v,min(f-sum,e[i].flow)); sum+=delta; e[i].flow-=delta; e[i^1].flow+=delta; if(f-sum<=0) break; } } if(!sum) dep[u]=-1; return sum; } int main() { scanf("%d%d",&n,&m); memset(dis,0x3f,sizeof(dis)); memset(g,-1,sizeof(-1)); for(int i=1,u,v,w,c;i<=m;i++) { scanf("%d%d%d%d",&u,&v,&w,&c); addg(u,v,w,c); } floyd(); printf("%d\n",dis[1][n]); for(int i=1;i<=m;i++) { int u=g[i].u,v=g[i].v,w=g[i].w,c=g[i].c; if(dis[1][u]+w+dis[v][n]==dis[1][n]) add(u,v,c); if(dis[1][v]+w+dis[u][n]==dis[1][n]) add(v,u,c);//如果这条边在最短路上,就加到最短路图中 } int ans=0; while(bfs()) { while(int temp=dfs(1,0x7fffffff)) ans+=temp; } printf("%d\n",ans); return 0; }