最短路+二分题目整理

科技资讯 投稿 5800 0 评论

最短路+二分题目整理

前往奥格瑞玛的道路

\(\qquad\题目要求最小化最大费用,显然是使用二分答案,二分答案首先应该看限制目标,此处的限制是血量限制,而目标是费用目标。这种情况我们可以二分费用,然后在图上跑最短路判定血量是否满足。
\(\qquad\对于check函数,我们去判定是否存在一条道路使得最高费用不超过mid,并且小号血量不超过hp,我们对于所有目的点费用不超过mid的点跑最短路,判定是否满足hp的限制即可。

代码

#include <bits/stdc++.h>
#define pb emplace_back

using namespace std;
using PII = pair<int, int>;

const int N = 1e5 + 10;
vector<PII> h[N];
int n, m, hp, hh, tt;
int cost[N], dist[N], st[N], q[N];

bool check(int mid {
	if (cost[1] > mid return 0;
	memset(dist, 0x3f, sizeof dist;
	memset(st, 0, sizeof dist;
	hh = tt = 0;
	q[0] = 1, st[1] = 1, dist[1] = 0;

	while (hh <= tt {
		int t = q[hh ++ ];
		st[t] = 0;
		for (auto k : h[t] {
			int v = k.first, w = k.second;
			if (cost[v] > mid continue ;
			if (dist[v] > dist[t] + w {
				dist[v] = dist[t] + w;
				if (!st[v] st[v] = 1, q[ ++ tt] = v;
			}
		}
	}

	return dist[n] <= hp;
}

int main( {
	scanf("%d%d%d", &n, &m, &hp;
	for (int i = 1; i <= n; i ++  scanf("%d", &cost[i];
	while (m --  {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w;
		h[u].pb(v, w, h[v].pb(u, w;
	}

	int l = 0, r = 1e9 + 1;
	while (l < r {
		int mid = l + r >> 1;
		if (check(mid r = mid;
		else l = mid + 1;
	}
	if (r == 1e9 + 1 puts("AFK";
	else printf("%d\n", r;

	return 0;
}

Telephone Lines S

题目链接
\(\qquad\我们可以取消 \(\le K\ 条道路的价值,并取得剩下的道路中最贵的。很容易想到一个贪心的策略,那就是取消最贵的 \(K\ 条路的价值,这样我们剩下的就是第 \(K + 1\ 贵的边了。
题目让我们最小化这个值,也就是让我们最小化 K + 1 大值,也可以采用二分。当然如果比它大的值小于 \(K\ 的时候,直接全部删除即可。
\(\qquad\二分之前我们同样先看下限制目标,这题的限制应该是更大值限制,而目标是最小费用目标,所以我们可以二分一下费用,并且判定是否在 \(1\sim N\的路上有不超过 \(K\ 条边比它大即可。

代码

#include <bits/stdc++.h>
#define pb emplace_back

using namespace std;
using PII = pair<int, int>;

const int N = 1010;
vector<PII> h[N];
int n, m, k, st[N], dist[N];

bool check(int mid {
	memset(dist, 0x3f, sizeof dist;
	memset(st, 0, sizeof st;
	queue<int> q;

	q.push(1, st[1] = 1, dist[1] = 0;
	while (q.size( {
		int t = q.front(;
		q.pop(, st[t] = 0;
		for (auto k : h[t] {
			int v = k.first, w = k.second;
			int W = w > mid;
			if (dist[v] > dist[t] + W {
				dist[v] = dist[t] + W;
				if (!st[v] st[v] = 1, q.push(v;
			}
		}
	}

	return dist[n] <= k;
}

int main( {
	scanf("%d%d%d", &n, &m, &k;
	while (m --  {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w;
		h[u].pb(v, w, h[v].pb(u, w;
	}

	int l = 0, r = 1e6 + 1;
	while (l < r {
		int mid = l + r >> 1;
		if (check(mid r = mid;
		else l = mid + 1;
	}
	printf("%d\n", r == 1e6 + 1 ? -1 : r;

	return 0;
}

Sightseeing Cows G

题目链接
\(\qquad\这题是平均值最小问题,可以二分平均值,没有特殊的限定条件。
\(\large mid\le \frac{\sum w}{\sum t}\Rightarrow mid\times \sum t \le \sum w\Rightarrow mid\times \sum t - \sum w <= 0\
所以我们是在原图上判负环和零环即可。

#include <bits/stdc++.h>
#define pb emplace_back

using namespace std;
using PII = pair<int, int>;

const int N = 1010;
vector<PII> h[N];
int n, m, st[N], f[N];
double dist[N];

bool dfs(int u, double mid {
	st[u] = 1;
	for (auto k : h[u] {
		int v = k.first, w = k.second;
		double W = w * mid - f[v];
		if (dist[v] >= dist[u] + W {
			dist[v] = dist[u] + W;
			if (st[v] { st[v] = 0; return 1; }
			if (dfs(v, mid { st[v] = 0; return 1; }
		}
	}
	return st[u] = 0;
}

bool check(double mid {
	int ring = 0;
	memset(st, 0, sizeof st;
	for (int i = 1; i <= n; i ++  {
		if (ring break ;
		if (st[i] continue ;
		ring |= dfs(i, mid;
	}
	return ring;
}

int main( {
	scanf("%d%d", &n, &m;
	for (int i = 1; i <= n; i ++  scanf("%d", &f[i];
	while (m --  {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w;
		h[u].pb(v, w;
	}

	double l = 0, r = 1001;
	while (r - l > 1e-4 {
		double mid = (l + r / 2;
		if (check(mid l = mid;
		else r = mid;
	}
	printf("%.2lf\n", r;

	return 0;
}

Word Rings

题目链接
\(mid\le \frac{\sum w}{cnt}\Rightarrow mid\times cnt\le \sum w\Rightarrow \sum (mid-w\le 0\
判一下零环和负环即可。
注意的是这里的建图方式。我们要求图上包含的信息是:点一定是给定字符串的头两个或尾两个字符;字符串的长度。我们由此可以想到只对于每个字符串连接首尾两个的字符,边权为字符串长度,然后与上题相同。

#include <bits/stdc++.h>
#define pb emplace_back

using namespace std;
using PII = pair<int, int>;

const int N = 1010;
vector<PII> h[N];
int n, m, st[N];
double dist[N];

bool dfs(int u, double mid {
	st[u] = 1;
	for (auto k : h[u] {
		int v = k.first, w = k.second;
		double W =  mid - w;
		if (dist[v] > dist[u] + W {
			dist[v] = dist[u] + W;
			if (st[v] { st[v] = 0; return 1; }
			if (dfs(v, mid { st[v] = 0; return 1; }
		}
	}
	return st[u] = 0;
}

bool check(double mid {
	int ring = 0;
	memset(st, 0, sizeof st;
	for (int i = 1; i <= 676; i ++  {
		if (ring break ;
		if (st[i] continue ;
		ring |= dfs(i, mid;
	}
	return ring;
}

int main( {
	while (scanf("%d", &n, n {
		for (int i = 1; i <= 676; i ++  h[i].clear(;
		string s;
		for (int i = 1; i <= n; i ++  {
			cin >> s;
			int a, b, sz = s.size(;
			a = (s[0] - 'a' * 26 + s[1] - 'a' + 1;
			b = (s[sz - 2] - 'a' * 26 + s[sz - 1] - 'a' + 1;
			h[a].pb(b, sz;
		}
		if (!check(0 puts("No solution";
		else {
			double l = 0, r = 100001;
			while (l < r - 1e-5 {
				double mid = (l + r / 2;
				if (check(mid l = mid;
				else r = mid;
			}
			printf("%.2lf\n", r;
		}
	}

	return 0;
}

编程笔记 » 最短路+二分题目整理

赞同 (35) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽