#bzoj3379#小G的烦恼(枚举顺序)

来源:互联网 发布:基础英语词汇知乎 编辑:程序博客网 时间:2024/06/05 04:27

的烦恼

【问题描述】

最近郁闷死了,MZ 想考考他的智商,给了他一道题,但是小G 退役了这么久,怎么可能做的出来啊?于是他跑去向 quack 大神求助,可是 quack 大神要打牌,于是找到了你,希望能够不让 MZ 失望。

问题是这样的:MZ 想去全球各地旅行。原本是有两家航空公司可以选择的,但是现在那两家公司合并了。然而,在合并初期,两家还没有交接好,于是出现了两家都要收钱的问题。由于 MZ 只想出去玩一个月,她可以选择包月机票(两家公司都有),对于其中的一家公来说,只要花费元,即可以在持有另一家公司的合法包月机票的情况下,乘坐任何票价不高于公司定价的元的飞机。另一家公司也是一样的。

简单来说,对于航线种价格 Pi  Qi,分别是公司和司的定价。假设你持有元的公司月票和元的公司月票,当Pi<=X 并且 Qi<=Y 时,你才可以乘坐航线 i。当然,如果单独购买这一趟航班的两张票也是可以的。

 现在 MZ 告诉你了 N-1 个她想要去的城市,MZ 初始时在号位置,并且告诉了你所有航线的两个价格。想要知道,最小的花费。

【输入】

第 1 行 2 个整数 N、M 分别表示城市的个数,航线的条数。

第 2-M+1 行,每行 4 个整数 ui,vi,Pi,Qi,分别表示航线的两个城市(飞机可以来回开),和两种价格。(可能存在重边和自环)

【输出】

共一行,一个整数 ANS,表示最小的花费

【样例输入】

5 5

1 2 3 2

1 3 2 4

2 4 4 2

5 3 3 3

1 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


这题当时的第一反应时二分答案,枚举一边再二分另一半,但是会T,很显然。

然后开始乱搞就想着两次二分,不过确实不太好说明正确性,而且确实不严谨,并且后来跑两趟

(外层二分p内q,外层二分q内p,然后两者取min好像也不是特别好说明为什么就能过,而且这题数据水)


正解是枚举,按照一定的分析和顺序枚举,很巧妙的枚举,也容易懂。

首先将p,q排序,然后外层从大到小枚举p的价格(外层q也是一样的),然后内层从小到大枚举q的价格,在第一次找到能够联通时,break,此时得到一组解

因为p是从大到小枚举的,q是从小到大枚举的,所以如果对于之前的较大的q,那种小的p都不能满足的话,那么后面的小q就更不能满足了,于是内层的p不用每次都从1开始,只需要从之前一次break的地方开始往后找就可以了。

时间复杂度O(2 * M * (N + M))


Code:

枚举:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;const int Maxn = 2000;const int Maxm = 5000;const int INF = 0x3f3f3f3f;struct FF{int cost, pos;bool operator < (const FF & X) const{return cost < X.cost;}bool operator == (const FF & X) const{return cost == X.cost;}}P[Maxm + 5], Q[Maxm + 5];struct node{int u, v, p, q;}Flt[Maxm + 5];struct E{int v, nxt;}edge[(Maxm << 1) + 5];int N, M, cnt;int fir[Maxn + 5];bool vis[Maxn + 5];bool getint(int & num){char c;int flg = 1;num = 0;while((c = getchar()) < '0' || c > '9'){if(c == '-')flg = -1;if(c == -1)return 0;}while(c >= '0' && c <= '9'){num = num * 10 + c - 48;if((c = getchar()) == -1)return 0;}num *= flg;return 1;}void addedge(int a, int b){edge[++ cnt].v = b, edge[cnt].nxt = fir[a], fir[a] = cnt;edge[++ cnt].v = a, edge[cnt].nxt = fir[b], fir[b] = cnt;}void Dfs(int x){vis[x] = 1;for(int i = fir[x]; i; i = edge[i].nxt)if(! vis[edge[i].v])Dfs(edge[i].v);}bool Check(int p, int q){for( ; q <= M && Q[q].cost == Q[q + 1].cost; ++ q);for( ; p <= M && P[p].cost == P[p + 1].cost; ++ p);cnt = 0;memset(fir, 0, sizeof fir );for(int i = 1; i <= M; ++ i)if(Flt[i].p <= P[p].cost && Flt[i].q <= Q[q].cost)addedge(Flt[i].u, Flt[i].v);memset(vis, 0, sizeof vis );Dfs(1);for(int i = 1; i <= N; ++ i)if(! vis[i])return 0;return 1;}int main(){//freopen("meizi.in", "r", stdin);//freopen("meizi.out", "w", stdout);getint(N), getint(M);for(int i = 1; i <= M; ++ i){getint(Flt[i].u), getint(Flt[i].v), getint(Flt[i].p), getint(Flt[i].q);P[i].cost = Flt[i].p, P[i].pos = i;Q[i].cost = Flt[i].q, Q[i].pos = i;}sort(P + 1, P + 1 + M);//int A = unique(P + 1, P + 1 + M) - P;sort(Q + 1, Q + 1 + M);//int B = unique(Q + 1, Q + 1 + M) - Q;int p = M, q = 1, Ans = INF;while(p){for( ; q <= M; ++ q)if(Check(p, q)){Ans = min(Ans, P[p].cost + Q[q].cost);break;}-- p;}printf("%d\n", Ans);return 0;}


附上乱搞完全不能保证正确性因数据水而跑得飞快的两趟二分代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;const int Maxn = 2000;const int Maxm = 5000;const int INF = 0x3f3f3f3f;struct FF{int cost, pos;bool operator < (const FF & X) const{return cost < X.cost;}}P[Maxm + 5], Q[Maxm + 5];struct node{int u, v, p, q;bool operator < (const node & X) const{return p + q < X.p + X.q;}}Flt[Maxm + 5];struct E{int v, nxt;}edge[(Maxm << 1) + 5];int N, M, cnt;int fir[Maxn + 5];bool vis[Maxn + 5];bool getint(int & num){char c;int flg = 1;num = 0;while((c = getchar()) < '0' || c > '9'){if(c == '-')flg = -1;if(c == -1)return 0;}while(c >= '0' && c <= '9'){num = num * 10 + c - 48;if((c = getchar()) == -1)return 0;}num *= flg;return 1;}void addedge(int a, int b){edge[++ cnt].v = b, edge[cnt].nxt = fir[a], fir[a] = cnt;edge[++ cnt].v = a, edge[cnt].nxt = fir[b], fir[b] = cnt;}void Dfs(int x){vis[x] = 1;for(int i = fir[x]; i; i = edge[i].nxt)if(! vis[edge[i].v])Dfs(edge[i].v);}bool Check(int p, int q){for( ; q <= M && Q[q].cost == Q[q + 1].cost; ++ q);cnt = 0;memset(fir, 0, sizeof fir );int B = Q[q].cost;for(int i = 1; i <= p; ++ i){int tmp = P[i].pos;if(Flt[tmp].q <= B)addedge(Flt[tmp].u, Flt[tmp].v);}memset(vis, 0, sizeof vis );Dfs(1);for(int i = 1; i <= N; ++ i)if(! vis[i])return 0;return 1;}int Find(int p){for(; p <= M && P[p].cost == P[p + 1].cost; ++ p);int dn = max(N - 1 - p, 0), up = M, mid, rt = INF;while(dn <= up){mid = (dn + up) >> 1;if(mid + p < N - 1)dn = mid + 1;else{if(Check(p, mid))rt = Q[mid].cost, up = mid - 1;else dn = mid + 1;}}if(rt != INF)return rt;else return 0x7f7f7f7f;}int Find1(int q){for(; q <= M && Q[q].cost == Q[q + 1].cost; ++ q);int dn = max(N - 1 - q, 0), up = M, mid, rt = INF;while(dn <= up){mid = (dn + up) >> 1;if(mid + q < N - 1)dn = mid + 1;else{if(Check(mid, q))rt = P[mid].cost, up = mid - 1;else dn = mid + 1;}}if(rt != INF)return rt;else return 0x7f7f7f7f;}int main(){//freopen("meizi.in", "r", stdin);//freopen("meizi.out", "w", stdout);getint(N), getint(M);for(int i = 1; i <= M; ++ i){getint(Flt[i].u), getint(Flt[i].v), getint(Flt[i].p), getint(Flt[i].q);P[i].cost = Flt[i].p, P[i].pos = i;Q[i].cost = Flt[i].q, Q[i].pos = i;}sort(P + 1, P + 1 + M);sort(Q + 1, Q + 1 + M);int dn = 1, up = M, mid, all = INF, b;while(dn <= up){mid = (dn + up) >> 1;b = Find(mid);if(P[mid].cost + b <= all)all = P[mid].cost + b, up = mid - 1;else dn = mid + 1;}int all2 = INF;dn = 1, up = M;while(dn <= up){mid = (dn + up) >> 1;b = Find1(mid);if(Q[mid].cost + b <= all2)all2 = Q[mid].cost + b, up = mid - 1;else dn = mid + 1;}printf("%d\n", min(all, all2));return 0;}









原创粉丝点击