2021.2.2图论专场

2020.2.2图论专场

写在前面

期望:130 实际:130
我的成绩感觉水分很足,因为我看了szt的博客,我错了,不会了

T1 公交车

题意:
\(n\)个结点和\(m\)条双向边,边有边权。给定一个\(S\),求S 到所有结点的最短路。还有 \(k\) 条公交路线,第 \(i\) 条路线有 \(t_i\) 个路口,路线是固定的,可以往返行驶,上第 \(i\) 条公交车需要 \(b_i\) 元的费用,但费用是一次性的。如果再次上车则需要再次付费。
数据范围:\(1≤n≤1e5, 1≤m≤2e5, 1≤k≤5e4, ∑ti≤2e5, 1≤k_i,b_i≤1e9\)
题解:
因为要求到所有点的最短路,直接上dij跑,那么如何处理公交线路?
最先到达的点所对应的公交线路一定是到这条公交线路的最近的点
考虑经过的次数:做一次车就能到达的点肯定不需要做两次,所以每条线路只可能经过一次
那么就可做了,只需要我们在跑最短路的时候顺便用遇到的公交线路更新一下就好了,顺便打个标记,避免用多次浪费时间
考场30pts代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#define orz cout << "AK IOI"<<"\n"

using namespace std;
const int maxn = 100010;
const int maxm = 200010; 
const long long inf = 214748364700;

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}
int n, m, k, s, mon[maxn], map[maxn][500];
struct edge{
	int u, v, w, nxt;
}e[maxm <<1];

int js, head[maxn];
void add(int u, int v, int w)
{
	e[++js].u = u;
	e[js].v = v;
	e[js].w = w;
	e[js].nxt = head[u];
	head[u] = js;
}
struct node{
	int now, w;
	bool operator < (const node &x)const
	{
		return w > x.w;
	}
};

long long dis[maxn]; 
bool vis[maxn];
priority_queue <node> q;
void dij(int s)
{
	for(int i = 1; i <= n; i++)
	dis[i] = inf;
	dis[s] = 0;
	q.push(node{s, 0});
	while(!q.empty())
	{
		node t = q.top();
		q.pop();
		int u = t.now;
		if(vis[u]) continue;
		vis[u] = 1;
		for(int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].v;
			if(dis[v] > dis[u] + e[i].w)
			dis[v] = dis[u] + e[i].w;
			q.push(node{v, dis[v]});
		}
	}
}
int main()
{
	freopen("transprt.in", "r", stdin);
	freopen("transprt.out", "w", stdout);
    n = read(), m = read(), k = read(), s = read();
    for(int i = 1; i <= m; i++)
    {
    	int u = read(), v = read(), w = read();
    	add(u, v, w);
    	add(v, u, w);
	}
	for(int i = 1; i <= k; i++)
	{
	    mon[i] = read();
	    int t = read();
		for(int i = 1; i <= t; i++)
		int a = read(); 
	}
	dij(s);
	for(int i = 1; i <= n; i++)
	printf("%lld ", dis[i]);
    fclose(stdin);
    fclose(stdout);
	return 0;
}

T2 灌溉

题意
Farmer John 有!个牧场,他希望灌溉他的所有牧场。牧场编号为1 ∼ !,要
灌溉一个牧场有两种方式, 一个是直接在这个牧场建设一个小型水库,另一个是
从别的牧场向这个牧场引水。 在第#个牧场建立小型水库需要9 % 美元, 而从第# 个
牧场向第:个牧场引水需要; %,< 美元。 即便牧场:没有建设小型水库, 只要有别的有
水的水库向它引水,那它自己也有水,而且可以向别的水库引水。请告诉 FJ 灌
溉所有牧场所需的最小代价。
题解
最小生成树,做过

#include <iostream>
#include <cstdio>
#include <cstring>
#define orz cout << "AK IOI"<<"\n"

using namespace std;
const int maxn = 310;
const int inf = 0x3f3f3f3f;

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * f;
}
int n, w[maxn], map[maxn][maxn], dis[maxn];
int ans, flag, minn;
bool vis[maxn];
void prim(int s)
{
	memset(dis, inf, sizeof(dis));
	dis[s] = 0;
	for(int i = 1; i <= n + 1; i++)
	{
	    minn = inf;	
	    for(int j = 1; j <= n + 1; j++)
	    if(vis[j] == 0 && minn > dis[j])
	    {
	    	flag = j;
	    	minn = dis[j];
		}
		vis[flag] = 1;
		ans += dis[flag];
		for(int j = 1; j <= n + 1; j++)
		{
			if(vis[j] == 0 && dis[j] > map[flag][j])
			dis[j] = map[flag][j];
		}
	}
}
int main()
{
	freopen("irrigate.in", "r", stdin);
	freopen("irrigate.out", "w", stdout);
	n = read();
	for(int i = 1; i <= n; i++)
	{
		w[i] = read();
		map[i][n + 1] = w[i];
		map[n + 1][i] = w[i];
	}
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		map[i][j] = read();
	}
	prim(n + 1);
	printf("%d", ans);
    fclose(stdin);
    fclose(stdout);
	return 0;
}

T3 对决

题意
有两个人在玩一个公平组合游戏。现在我们要分析这两个人谁能获胜。给定
这个游戏的所有局面和局面间的转移,显然它会构成一个 DAG。
如果一个局面不能转移到其它局面,那么称这个局面为终止局面;如果一个
局面不能由其它局面转移而来,那么称这个局面为起始局面。
已知这个游戏有!个局面和"条转移 (即 DAG 有!个点和"条边) , 有唯一的
起始局面,但可能有多个终止局面。同时,每一个局面都可以由初始局面到达。
现在告诉你所有终止局面是哪方获胜, 你能否求出每个局面是先手必胜态还是先
手必败态?
题解
博弈论,我不会,他不配!

posted @ 2021-02-02 21:03  _程门立雪  阅读(4)  评论(0编辑  收藏  举报