专题三:bfs、图论专题(1.cf)

其他

挑战程序设计竞赛

1.POJ 3259 Wormholes

  • 这道题后来我又想了一个方法,只能说这个方法不是完全正确,还要因题而定。为什么这么说呢?我用的是Bellman-Ford找负圈,可以这么说,若存在负圈,且这个负圈和起点是连通的,则此法是正确的。可见,此题这个方法是可以的。这个方法很快,用邻接表也是很快很方便的,只用160ms。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int N, E, M, W, T;
const int maxn = 5205;
struct edge
{
    int from, to, cost;
};
edge es[5205];
int d[maxn];
bool find_negative_loop()
{
    memset(d, 0, sizeof(d));
    for(int i = 0; i < N; i++){
        for(int j = 0; j < E; j++){
            edge e = es[j];
            if(d[e.to] > d[e.from] + e.cost){
                d[e.to] = d[e.from] + e.cost;
                if(i == N - 1) return true;
            }
        }
    }
    return false;
}
int main()
{
    scanf("%d", &T);
    while(T--){
        scanf("%d%d%d", &N, &M, &W);
        E = 2 * M + W;
        int s, t, co;
        for(int i = 0; i < M; i++){
            scanf("%d%d%d", &s, &t, &co);
            es[i] = {s, t, co};
            es[i + M] = {t, s, co};
        }
        for(int i = 2 * M; i < W + 2 * M; i++){
            scanf("%d%d%d", &s, &t, &co);
            es[i] = {s, t, -co};
        }
        if(find_negative_loop()) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
  • 当然还有法二,用Floyd-Warshall算法。但是比较慢,1600ms。有一些注意事项:这个题,首先要提醒,可能出现重边,要选择最小的那条边。其次,我发现把min换成if判断语句,时间至少快了400MS。 欸,输出是"YES\n"还是"Yes\n"啊,看清楚行不!
#include<cstdio>
using namespace std;
int F, N, M, W;
const int INF = 1e8;
int cost[505][505];
 
void solve()
{
    for(int k = 1; k <= N; k++){
        for(int i = 1; i <= N; i++){
            for(int j = 1; j <= N; j++){
                if(cost[i][j] > cost[i][k] + cost[k][j]){
                    cost[i][j] = cost[i][k] + cost[k][j];
                }
            }
            if(cost[i][i] < 0){
                printf("YES\n");
                return;
            }
        }
    }
    printf("NO\n");
}
int main()
{
    scanf("%d", &F);
    int s, e, t;
    while(F--){
        scanf("%d%d%d", &N, &M, &W);
        for(int i = 1; i <= N; i++){
            for(int j = 1; j <= N; j++){
                cost[i][j] = INF;
            }
            cost[i][i] = 0;
        }
        //把这个cost[i][i] = 0放前面,万一只有一个负环怎么办?
        for(int i = 1; i <= M; i++){
            scanf("%d%d%d", &s, &e, &t);
            if(cost[s][e] > t){
                cost[s][e] = cost[e][s] = t;
            }
        }
        for(int i = 1; i <= W; i++){
            scanf("%d%d%d", &s, &e, &t);
            cost[s][e] = -t;
        }
        solve();
    }
    return 0;
}

kuangbin系列

1.POJ 2253 Frogger

  • 二分法加并查集好像还挺常用的,因此把这道题记录下来。
  • 感觉二分法加并查集经常用于最大化最小值,而且不用将每个点都连起来,也不关心总和是否最小。
//这道题并不难,但并非是寻找最短路,而是最小化最大值,因此看到这里想到了二分法加并查集。
//最后还是想说一句,仔细读题,别总是因为输出格式不对而犯错误。
//复杂度应该是n^2*log(n)。
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 205;
int par[maxn], ranking[maxn];
void init(int n)
{
    for(int i = 0; i < n; i++){
        par[i] = i;
        ranking[i] = 0;
    }
}
int find(int x)
{
    if(par[x] == x){
        return x;
    }
    else{
        return par[x] = find(par[x]);
    }
}
void unite(int x, int y)
{
    x = find(x);
    y = find(y);
    if(x == y){
        return;
    }
    if(ranking[x] < ranking[y]){
        par[x] = y;
    }
    else{
        par[y] = x;
        if(ranking[x] == ranking[y]){
            ranking[x]++;
        }
    }
}
bool same(int x, int y)
{
    return find(x) == find(y);
}

int N, kase;
int x[maxn], y[maxn];
double dis(int m, int n)
{
    double dx = (x[m] - x[n]) * (x[m] - x[n]), dy = (y[m] - y[n]) * (y[m] - y[n]);
    return sqrt(dx + dy);
}
bool C(double x)
{
    init(N);
    for(int i = 0; i < N; i++){
        for(int j = i + 1; j < N; j++){
            if(!same(i, j) && dis(i, j) <= x){
                unite(i, j);
            }
        }
    }
    return same(0, 1);
}
void solve()
{
    double lb = 0, ub = 1e9;
    for(int i = 0; i < 100; i++){
        double mid = (lb + ub) / 2;
        if(C(mid)){
            ub = mid;
        }
        else{
            lb = mid;
        }
    }
    printf("Scenario #%d\nFrog Distance = %.3f\n\n", ++kase, lb);
}
int main()
{
    while(scanf("%d", &N) && N){
        for(int i = 0; i < N; i++){
            scanf("%d%d", &x[i], &y[i]);
        }
        solve();
    }
    return 0;
}

POJ 1502 MPI Maelstrom

  • (1)题意很长很扯淡,其实题的意思很简单。这道题输入的处理还是有一些难的,但是有很巧妙的方法。需要指出的是,atoi()这个函数很有意思,可以把字符串边成整数,但是也是有大小限制的,返回值是int。在中就有定义。不得不说,里面真是啥都有,写代码时顺便把他带上也挺好的。
  • (2)用dijstra算法一定要注意P那个地方,first和second别写反了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 105, INF = 1e8;
int N;
struct edge
{
    int to, cost;
};
vector<edge> G[maxn];
char str[15];
int d[maxn];
typedef pair<int, int> P;
void solve()
{
    fill(d, d + N, INF);
    d[0] = 0;
    priority_queue<P, vector<P>, greater<P> > que;
    que.push(P(0, 0));
    while(!que.empty()){
        P p = que.top();
        que.pop();
        int v = p.second;
        if(d[v] < p.first){
            continue;
        }
        int sz = G[v].size();
        for(int i = 0; i < sz; i++){
            edge e = G[v][i];
            if(d[e.to] > d[v] + e.cost){
                d[e.to] = d[v] + e.cost;
                que.push(P(d[e.to], e.to));
            }
        }
    }
    int res = *max_element(d, d + N);
    printf("%d\n", res);
}
int main()
{
    scanf("%d", &N);
    for(int i = 1; i < N; i++){
        for(int j = 0; j < i; j++){
            scanf("%s", str);
            if(str[0] == 'x'){
                continue;
            }
            int co = atoi(str);
            G[i].push_back({j, co});
            G[j].push_back({i, co});
        }
    }
    solve();
    return 0;
}

POJ 1511 Invitation Cards

  • (1)这道题数据大的扯淡,用邻接表,即挑战程序设计竞赛上面教的方法直接超时。我猜测要用Spfa算法。

POJ 3159 Candies

  • (1)又是尼玛超时呀,但是这家活用差分约束就没事呀,听不懂呀,当当当挡,传送门POJ3159 Candies 差分约束。

cf

1.K. Birdwatching

  • 如果满足题意的点得话,那么只能染成一种颜色才对。但是染了两种颜色,说明是从“其他点”(这里指与题目给的起点的相邻的点)走过来的。因为建了反图,因此从这个点可以走到那个“其他点”。即从这个点不止一条路径可以到题目给的那个起点。

  • 其实一个点只需要染出两个颜色就可以断言不满足条件。因此只开两个color数组就行,简化步骤。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 100010;
int h[maxn], e[maxn], ne[maxn], idx, N, M, K;
int color1[maxn], color2[maxn];
vector<int> ans;
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int c) {
	//染色是不允许通过树根的!勿忘!
	if (u == K) return;
	if (color1[u] == -1) color1[u] = c;
	else if (color2[u] == -1 && color1[u] != c) color2[u] = c;
	else return;
	for (int i = h[u]; i != -1; i = ne[i]) {
		int v = e[i];
		dfs(v, c);
	}
}
int main() {
	memset(h, -1, sizeof h);
	memset(color1, -1, sizeof color1);
	memset(color2, -1, sizeof color2);
	scanf("%d%d%d", &N, &M, &K);
	for (int i = 0; i < M; i++) {
		int a, b;
		scanf("%d%d", &a, &b);
		add(b, a);
	}
	for (int i = h[K]; i != -1; i = ne[i]) {
		dfs(e[i], e[i]);
	}
	for (int i = h[K]; i != -1; i = ne[i]) {
		if (color1[e[i]] != -1 && color2[e[i]] == -1) ans.push_back(e[i]);
	}
	sort(ans.begin(), ans.end());
	printf("%d\n", ans.size());
	for (auto p : ans) {
		printf("%d\n", p);
	}
	return 0;
}

2.C Coolest Ski Route

  • 这道题是一个有向无环图,求路径最大长度。可以用动态规划+拓扑排序来做!这其实是一种新思路。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1010, maxm = 5010;
int f[maxn], e[maxm], w[maxm], h[maxm], ne[maxm], idx, N, M;
int dp(int u) {
	int& v = f[u];
	if (v != -1) return v;
	v = 0;
	for (int i = h[u]; i != -1; i = ne[i]) {
		v = max(v, dp(e[i])+ w[i]) ;
	}
	return v;
}
void add(int a, int b, int c) {
	e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int main() {
	memset(h, -1, sizeof h);
	memset(f, -1, sizeof f);
	scanf("%d%d", &N, &M);
	for (int i = 0; i < M; i++) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	int res = 0;
	for (int i = 1; i <= N; i++) {
		res = max(res, dp(i));
	}
	printf("%d\n", res);
	return 0;
}

3.I Iron and Coal

  • 这道题啊,是找到一个节点,使得到起点,煤矿和铁矿的距离之和最短。
  • 第一步很好找,就是从起点开始搜单源最短距离。
  • 第二步,建立虚拟原点和反图,进行两次宽度优先搜索。然后把三个距离相加找最小值。
  • 虚拟节点:建一个节点,到多个源点的距离为0.
  • 注意一定要判断三个dist是否都为INF。不要直接相加,不然会爆int。
  • 这道题下次再用虚拟节点的时候用BFS!别用dijstra
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 100010, maxm = 1000010, INF = 0x3f3f3f3f;
int dist[maxn], dist1[maxn], dist2[maxn];
int h1[maxn], e1[maxm], ne1[maxm], idx1;
int h2[maxn], e2[maxm], ne2[maxm], idx2;
int iron[maxn], coal[maxn];
int N, M, K;
bool vis[maxn], vis1[maxn], vis2[maxn];
typedef pair<int, int> P;
void add(int a, int b) {
	e1[idx1] = b, ne1[idx1] = h1[a], h1[a] = idx1++;
	e2[idx2] = a, ne2[idx2] = h2[b], h2[b] = idx2++;
}
void bfs1() {
	for (int i = 1; i <= N; i++) dist[i] = INF;
	priority_queue<P, vector<P>, greater<P> > que;
	dist[1] = 0;
	que.push({ 0, 1 });
	while (que.size()) {
		P p = que.top(); que.pop();
		int u = p.second; int d = p.first;
		if (vis[u]) continue;
		vis[u] = 1;
		for (int i = h1[u]; i != -1; i = ne1[i]) {
			int v = e1[i];
			if (dist[v] > d + 1) {
				dist[v] = d + 1;
				que.push({ dist[v], v });
			}
		}
	}
}
void bfs2(int d[], int sz, int matter[], bool vis[]) {
	for (int i = 1; i <= N; i++) d[i] = INF;
	priority_queue<P, vector<P>, greater<P> > que;
	for (int i = 0; i < sz; i++) {
		d[matter[i]] = 0;
		que.push({ 0, matter[i] });
	}
	while (que.size()) {
		P p = que.top(); que.pop();
		int u = p.second; int dist = p.first;
		if (vis[u]) continue;
		vis[u] = true;
		for (int i = h2[u]; i != -1; i = ne2[i]) {
			int v = e2[i];
			if (d[v] > dist + 1) {
				d[v] = dist + 1;
				que.push({ d[v], v });
			}
		}
	}
}
int main() {
	memset(h1, -1, sizeof h1);
	memset(h2, -1, sizeof h2);

	scanf("%d%d%d", &N, &M, &K);
	for (int i = 0; i < M; i++) {
		scanf("%d", &iron[i]);
	}
	for (int i = 0; i < K; i++) {
		scanf("%d", &coal[i]);
	}
	for (int i = 1; i <= N; i++) {
		int a, b;
		scanf("%d", &a);
		for (int j = 0; j < a; j++) {
			scanf("%d", &b);
			add(i, b);
		}
	}
	bfs1();
	bfs2(dist1, M, iron, vis1);
	bfs2(dist2, K, coal, vis2);
	int ans = INF;
	for (int i = 1; i <= N; i++) {
		if (dist[i] != INF && dist1[i] != INF && dist2[i] != INF) {
			ans = min(ans, dist[i] + dist1[i] + dist2[i]);
		}
	}
	if (ans == INF) printf("impossible\n");
	else printf("%d\n", ans);
	return 0;
}

4.H Hyacinth

  • 这道题,用染色很简单。因为dfs可以保证向一个方向往前跑。因此,每次都把一个结点的第一个颜色染成上一个节点的第二个颜色。第二个颜色始终染成cnt++。
  • 因为这道题最开始dfs的节点不会有前驱节点,需要直接染上两个颜色(1,2)。但是还要注意:如果这个节点出度为1,就要把下一个节点两个颜色都要染成这两个颜色(1和2)。否则,染成1和cnt++。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 10010, maxm = 20010;
int h[maxn], ne[maxm], e[maxm], idx, degree[maxn];
int N, cnt = 3;
int color1[maxn], color2[maxn];
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u) {
	for (int i = h[u]; i != -1; i = ne[i]) {
		int v = e[i];
		if (color1[v] && color2[v]) continue;
		else{
			color1[v] = color2[u];
			color2[v] = cnt++;
			dfs(v);
		}
	}
}
int main() {
	memset(h, -1, sizeof h);
	scanf("%d", &N);
	for (int i = 1; i < N; i++) {
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
		degree[a]++, degree[b]++;
	}
	int v = e[h[1]];
	color1[1] = color1[v] = 1;
	color2[1] = 2;
	if (degree[1] == 1) color2[v] = 2;
	else color2[v] = cnt++;
	dfs(1);
	dfs(v);
	for (int i = 1; i <= N; i++) {
		printf("%d %d\n", color1[i], color2[i]);
	}
	return 0;
}

5.D Digi Comp II

  • 这道题呀,和刘汝佳的那个题很相似,都是一堆小球从1号节点滚下来,每个节点都标注着’L’和’R’。但是这个题不是二叉树,只是一个出度为2的很普通的树。
  • 在bfs一开始的地方,把所有入度为0的节点全部推入que。一开始我还在想这个有必要嘛?其实是很有必要的!因为中间有一步是入度为0的时候才会推入que。如果一开始不把入度为0的点推入que,有些节点的入度就降不到0了。
  • 看到那个static que que了吧。声明为静态局部变量,可以防止栈溢出。不过要注意得把que清空。
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 500010;
ll balls[maxn];
int g[maxn][2], in[maxn];
bool st[maxn];
int N;
ll M;
void bfs() {
	//用静态局部变量,防止栈溢出!
	static queue<int> que;
	while (que.size()) que.pop();
	for (int i = 1; i <= N; i++) {
		if (in[i] == 0) que.push(i);
	}
	while (que.size()) {
		int u = que.front(); que.pop();
		ll n = balls[u];
		bool flag = st[u];
		if (n % 2) st[u] = !st[u];
		int l = g[u][0], r = g[u][1];

		if (l) {
			if (!flag) balls[l] += (n + 1) / 2;
			else balls[l] += n / 2;
			in[l]--;
			if (in[l] == 0) que.push(l);
		}
		if (r) {
			if (!flag) balls[r] += n / 2;
			else balls[r] += (n + 1) / 2;
			in[r]--;
			if (in[r] == 0) que.push(r);
		}
	}
}
int main() {
	char op[5];
	scanf("%lld%d", &M, &N);
	for (int i = 1; i <= N; i++) {
		int a, b;
		scanf("%s%d%d", op, &a, &b);
		g[i][0] = a;
		g[i][1] = b;
		in[a]++, in[b]++;
		if (op[0] == 'L') st[i] = false;
		else st[i] = true;
	}
	balls[1] = M;
	bfs();
	for (int i = 1; i <= N; i++) {
		printf("%c", st[i] ? 'R' : 'L');
	}
	return 0;
}

6.B. Two Buttons

法一:

*这道题可以用建图的方法去写。建立 1 0 4 10^4 104个节点, 2 ∗ 1 0 4 2 * 10 ^4 2104条边即可。然后用Dij算法就可以很快的求出来。

  • 因为这道题不可能经过节点数字超过10000的情况,因此10000以上是不用建图的。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 20010, maxm = 40010, INF = 0x3f3f3f3f;
int h[maxn], e[maxm], ne[maxm], idx;
int d[maxn], N, M;
bool vis[maxn];
typedef pair<int, int> P;
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int bfs() {
	fill(d, d + maxn, INF);
	static priority_queue<P, vector<P>, greater<P> > que;
	while (que.size()) que.pop();
	que.push({0, N});
	d[N] = 0;
	while (que.size()) {
		auto p = que.top(); que.pop();
		int u = p.second, dist = p.first;
		if (vis[u]) continue;
		vis[u] = true;
		for (int i = h[u]; i != -1; i = ne[i]) {
			int v = e[i];
			if (d[v] > dist + 1) {
				d[v] = dist + 1;
				que.push({ d[v], v });
			}
		}
	}
	return d[M];
}
int main() {
	scanf("%d%d", &N, &M);
	memset(h, -1, sizeof h);
	for (int i = 1; i <= 10000; i++) {
		if (i - 1 > 0) add(i, i - 1);
		if (i * 2 <= 10000) add(i, i * 2);
	}
	int ans = bfs();
	printf("%d\n", ans);
	return 0;
}
法二:
  • 由于我们刚才分析了,一定是从下面乘2这样趋近是最快的。我们来反着想,由m到n,也就是m只能加1或者除以2。如果m是奇数,那么必然最后一步是减操作,我们可以确定,因此可以先加回去m变成m+1。如果是偶数,那么必然最后一步是乘以2也可以还原,把m变成m/2
#include<cstdio>
#include<algorithm>
using namespace std;

int main() {
	int N, M;
	scanf("%d%d", &N, &M);
	int cnt = 0;
	if (N >= M) printf("%d", N - M);
	else {
		while (M > N) {
			if (M % 2) cnt++, M++;
			cnt++;
			M /= 2;
		}
		printf("%d\n", cnt + N - M);
	}
	return 0;
}

7.B. Two Buttons

  • 这道题就是说把N变成M,然后N只能减1或是乘2。这道题我一开始用的是最短路。其实可以简单的用bfs就可以模拟出这个过程。而且复杂度也降低了很多。
  • 这道题,最短路路径的坐标最大就是M+1,不可能是更大的坐标
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 20010, INF = 0x3f3f3f3f;
int d[maxn], N, M;
void bfs() {
	queue<int> que;
	fill(d, d + maxn, INF);
	d[N] = 0;
	que.push(N);
	while (que.size()) {
		int u = que.front(); que.pop();
		if (2 * u <= 2 * M && d[2 * u] > d[u] + 1) {
			d[2 * u] = d[u] + 1;
			que.push(2 * u);
		}
		if (u > 1 && d[u - 1] > d[u] + 1) {
			d[u - 1] = d[u] + 1;
			que.push(u - 1);
		}
	}
}
int main() {
	scanf("%d%d", &N, &M);
	bfs();
	printf("%d\n", d[M]);
	return 0;
}

8.B. Hierarchy

  • 只要除了最高领导者之外,每个人有且仅有一个上司,我们就构建出来了一个树。因此,就可以用最小生成树去写。只有一点需要变动,就是加一个判断,一个人有且仅有有一个上司,否则continue
typedef long long ll;
int q[maxn], N, M, p[maxn];
bool has_parents[maxn], one_sup[maxn];
void init(int N) {
	for (int i = 1; i <= N; i++) p[i] = i;
}
int find(int x) {
	if (p[x] == x) return x;
	return p[x] = find(p[x]);
}
void unite(int x, int y) {
	if (find(x) == find(y)) return;
	p[find(x)] = find(y);
}
struct P {
	int u, v, w;
	bool operator <(const P& rhp)const {
		return w < rhp.w;
	}
}G[maxm];
ll solve() {
	init(N);
	int cnt = 0;
	for (int i = 1; i <= N; i++) {
		cnt += !has_parents[i];
	}
	if (cnt != 1) return -1;
	ll res = 0;
	sort(G, G + M);
	for (int i = 0; i < M; i++) {
		int u = G[i].u, v = G[i].v;
		if (find(u) == find(v) || one_sup[v]) continue;
		unite(u, v);
		res += G[i].w;
		one_sup[v] = true;
	}
	return res;
}

9.A. The Two Routes

题意:告诉你n个点的完全图,有m条边是铁路,其余的边是马路,一个人走铁路一个人走马路,问两个人都到达点n的时间。

  • cf蒙人题。1个N之间必然直接连接一条路,要么是公路,要么是铁路。即肯定有1种方式会直接到终点。根本就不会相撞。剩下只需要看看剩下的那个人到达N的时间即可。
  • 但是有一个需要注意,需要修改Dijkstra朴素算法,其实只需要在里面加一句判断就行:if (g[t][j] != k) continue;
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 405, INF = 1e9;
int g[maxn][maxn], N, M, d[maxn];
bool vis[maxn];
void bfs(int k) {
	fill(d, d + maxn, INF);
	d[1] = 0;
	for (int i = 0; i < N; i++) {
		int t = -1;
		for (int j = 1; j <= N; j++) {
			if (!vis[j] && (t == -1 || d[t] > d[j])) t = j;
		}
		vis[t] = true;
		for (int j = 1; j <= N; j++) {
			if (g[t][j] != k) continue;
			d[j] = min(d[j], d[t] + 1);
		}
	}
	if (d[N] != INF) printf("%d\n", d[N]);
	else printf("-1\n");
}
int main() {
	scanf("%d%d", &N, &M);
	for (int i = 0; i < M; i++) {
		int a, b;
		scanf("%d%d", &a, &b);
		g[a][b] = g[b][a] = 1;
	}
	bfs(!g[1][N]);
	return 0;
}

10.A. Fair

  • 再甩一道虚拟节点的题目。其实,虚拟节点啥也不是,就是辅助bfs理解的。但是,bfs其实和迷宫那个模板是一模一样的。其实网上动不动都说bfs,用的都是迷宫那个模板的改编。
  • 注意,这种题都是反向求解,找到每个货物到城镇的最短距离,也就是bfs搜索k次。然后,对于每个城镇,把这K个结果加起来。
  • 可以用nth_element这个STL,具体的可以搜一搜(收藏里面有),很方便。
  • 注意这种题再相加距离的时候一定要注意INF,因为可能会撑爆int。
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 100010, maxm = 200010, INF = 1e9;
int h[maxn], e[maxm], ne[maxm], idx;
int d[105][maxn], N, M, K, S;
bool vis[maxn];
vector<int> kinds[105];
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void bfs(int k) {
	memset(vis, 0, sizeof vis);
	fill(d[k], d[k] + N + 1, INF);
	queue<int> que;
	for (auto p : kinds[k]) {
		vis[p] = true;
		d[k][p] = 0;
		que.push(p);
	}
	while (que.size()) {
		int u = que.front();
		que.pop();
		for (int i = h[u]; i != -1; i = ne[i]) {
			int v = e[i];
			if (vis[v]) continue;
			d[k][v] = d[k][u] + 1;
			vis[v] = true;
			que.push(v);
		}
	}
}
int main() {
	scanf("%d%d%d%d", &N, &M, &K, &S);
	for (int i = 1; i <= N; i++) {
		int x;
		scanf("%d", &x);
		kinds[x].push_back(i);
	}
	memset(h, -1, sizeof h);
	for (int i = 0; i < M; i++) {
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
	}
	for (int i = 1; i <= K; i++) {
		bfs(i);
	}
	int total[105];
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= K; j++) {
			total[j] = d[j][i];
		}
		nth_element(total + 1, total + S + 1, total + K + 1);
		int res = 0;
		for (int j = 1; j <= S; j++) {
			res += total[j];
		}
		printf("%d ", res);
	}
	return 0;
}

11.903. 昂贵的聘礼

  • 其实可以增加一个虚拟源点,让这个源点到每个物品的距离就是直接购买的价钱。这样子的话,起点就是虚拟源点(可以设为0),终点就是1(酋长的女儿)。
  • 或者是将酋长当作起点,最后只需要 ans = min(ans, d[i] + cost[i]).
  • 但是等级没有转换对。其实只需要枚举区间就行,就是[grade[i] - M, grade[1]] ~ [grade[1], grade[1] + M].
  • 多次循环的话,初始化千万别忘!
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn = 110, INF = 1e9;
int g[maxn][maxn], N, M, d[maxn], cost[maxn], grade[maxn];
bool vis[maxn];
int bfs(int l, int r) {
	fill(d, d + maxn, INF);
	memset(vis, false, sizeof vis);
	d[1] = 0;
	for (int i = 0; i < N; i++) {
		int t = -1;
		for (int j = 1; j <= N; j++) {
			if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
		}
		vis[t] = true;
		for (int j = 1; j <= N; j++) {
			if (l <= grade[j] && grade[j] <= r) {
				d[j] = min(d[j], d[t] + g[t][j]);
			}
		}
	}
	int ans = cost[1];
	for (int i = 1; i <= N; i++) {
		if (d[i] == INF) continue;
		ans = min(ans, d[i] + cost[i]);
	}
	return ans;
}
int main() {
	scanf("%d%d", &M, &N);
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) g[i][j] = INF;
	}
	for (int i = 1; i <= N; i++) {
		int P, L, X;
		scanf("%d%d%d", &P, &L, &X);
		cost[i] = P, grade[i] = L;
		for (int j = 0; j < X; j++) {
			int T, V;
			scanf("%d%d", &T, &V);
			g[i][T] = min(g[i][T], V);
		}
	}
	int ans = INF;
	for (int i = 0; i <= M ; i++) {
		ans = min(ans, bfs(grade[1] - M + i, grade[1] + i));
	}
	printf("%d\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值