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

的烦恼

【问题描述】

最近郁闷死了,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;
}	









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值