【问题描述】小 G 最近郁闷死了,MZ 想考考他的智商,给了他一道题,但是小G 退役了这么久,怎么可能做的出来啊?于是他跑去向 quack 大神求助,可是 quack 大神要打牌,于是找到了你,希望能够不让 MZ 失望。问题是这样的:MZ 想去全球各地旅行。原本是有两家航空公司可以选择的,但是现在那两家公司合并了。然而,在合并初期,两家还没有交接好,于是出现了两家都要收钱的问题。由于 MZ 只想出去玩一个月,她可以选择包月机票(两家公司都有),对于其中的一家公司 A 来说,只要花费 X 元,即可以在持有另一家公司的合法包月机票的情况下,乘坐任何票价不高于 A 公司定价的 X 元的飞机。另一家公司也是一样的。简单来说,对于航线 i 有 2 种价格 Pi 和 Qi,分别是 A 公司和 B 公司的定价。假设你持有 X 元的 A 公司月票和 Y 元的 B 公司月票,当Pi<=X 并且 Qi<=Y 时,你才可以乘坐航线 i。当然,如果单独购买这一趟航班的两张票也是可以的。现在 MZ 告诉你了 N-1 个她想要去的城市,MZ 初始时在 1 号位置,并且告诉了你所有航线的两个价格。想要知道,最小的花费。
【输入】
第 1 行 2 个整数 N、M 分别表示城市的个数,航线的条数。
第 2-M+1 行,每行 4 个整数 ui,vi,Pi,Qi,分别表示航线的两个城市(飞机可以来回开),和两种价格。(可能存在重边和自环)
【输出】
共一行,一个整数 ANS,表示最小的花费。
【样例输入】5 51 2 3 21 3 2 42 4 4 25 3 3 31 4 0 1
【样例输出】7
【样例解释】选择除了 2-4 的所有边,所以答案是 3+4=7
【数据范围】
对于 20%的数据 N,M <= 15
对于 50%的数据 N <= 200, M <= 500对于 80%的数据 N <= 500, M <= 1000对于 100%的数据 N <= 2000, M <= 5000, Pi, Qi <= 10^9
题解:先不下降地枚举一个月票的价格p之后,再不上升地枚举另外一张月票q的价格。因为题目要求求最小值,且p单调不下降,那么随着q的减小,满足的线路一定会越来越少,当一个p价格对于当前的q价格不满足时,它对于之后的q价格也一定不会满足,所以不需重复枚举。最后判断图是否连通就可以了(这里是用最小生成树,但据说BFS或DFS会更快一点)。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; const int N=2010; const int M=5010; int n, m, h[N], fa[N]; int sum, pw[M], qw[M]; struct node{ int s, e, pw, qw; }edge[M]; bool Kruskal( int pw, int qw ) { memset( h, 0, sizeof h ); memset( fa, -1, sizeof fa ); int cnt=0; for( int i=1; i<=m; i++ ) if( edge[i].pw<=pw && edge[i].qw<=qw ) { int s=edge[i].s, e=edge[i].e; while( fa[s]!=-1 ) s=fa[s]; while( fa[e]!=-1 ) e=fa[e]; if( s!=e ) { if( h[s]<h[e] ) fa[s]=e; else fa[e]=s; if( h[s]==h[e] ) h[s]++; cnt++; } if( cnt==n-1 ) return 1; } return 0; } int main() { scanf( "%d%d", &n, &m ); for( int i=1; i<=m; i++ ) { scanf( "%d%d", &edge[i].s, &edge[i].e ); scanf( "%d%d", &edge[i].pw, &edge[i].qw ); pw[i]=edge[i].pw; qw[i]=edge[i].qw; } sort( pw+1, pw+m+1 ); sort( qw+1, qw+m+1 ); sum=INF; for( int i=n-1, j=m; i<=m; i++ ) for( ; j&& Kruskal( pw[i], qw[j] ); j-- ) sum=min( sum, pw[i]+qw[j] ); printf( "%d\n", sum ); return 0; }
当然也有更简单的思路,只是跑得比较慢。将边按p排序,然后枚举q,判断图是否连通。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; const int INF=0x3f3f3f3f; const int N=2010; const int M=5010; int n, m, h[N], fa[N]; int sum, qw[M]; struct node{ int s, e, pw, qw; }edge[M]; bool cmp( node a, node b ) { return a.pw<b.pw; } int Kruskal( int qw ) { memset( h, 0, sizeof h ); memset( fa, -1, sizeof fa ); int cnt=0, maxp=0; for( int i=1; i<=m; i++ ) if( edge[i].qw<=qw ) { int s=edge[i].s, e=edge[i].e; while( fa[s]!=-1 ) s=fa[s]; while( fa[e]!=-1 ) e=fa[e]; if( s!=e ) { if( h[s]<h[e] ) fa[s]=e; else fa[e]=s; if( h[s]==h[e] ) h[s]++; cnt++; maxp=max( maxp, edge[i].pw ); } if( cnt==n-1 ) return maxp; } return INF; } int main() { scanf( "%d%d", &n, &m ); for( int i=1; i<=m; i++ ) { scanf( "%d%d", &edge[i].s, &edge[i].e ); scanf( "%d%d", &edge[i].pw, &edge[i].qw ); qw[i]=edge[i].qw; } sort( edge+1, edge+m+1, cmp ); sum=INF; for( int i=1; i<=m; i++ )//枚举qw sum=min( sum, qw[i]+Kruskal( qw[i] ) ); printf( "%d\n", sum ); return 0; }
[BZOJ3379]一套NOIP膜你题:小G的烦恼
最新推荐文章于 2018-11-19 09:34:39 发布