6.26林大实验室训练补题(27/400)

来的十分之晚,可能是重复做一件事情太过疲惫,效率下降很多,心理上的驱动力也下降许多,希望尽快调整过来
分析题目,写代码很快,五分之四的时间都用来debug之中,效率好低,思考一下,如何解决
算是补了两道题目,图论为主,刚补完感想蛮多的,过了两天再来写博客,就没啥感觉了,当题解写

旅行2

在这里插入图片描述

分析:

看到这个题目第一眼的时候,我完全看不出是最小生成树的题目(打完今天的百度之星就去ACWing刷一下最小生成树),然后十分开心的告诉GO利特,搜索跑,记一下路径上的最大值和最小值不就行了!噩梦的开始于是,开始愚蠢的跑dfs,显然,dfs会重复搜索很多路径,时间复杂度难以承受,于是思考记忆化搜索,每个点记忆的内容,是由这个点,到终点的最大值和最小值,更新的时候,只需要不断地拼接即可。
于是便有

#include <bits/stdc++.h>
using namespace std;

const int N = 510;

int n, m;
int S, T;

int gcd(int a, int b) {
	if (b == 0)
		return a;
	return gcd(b, a % b);
}

typedef pair<int, int> PII;
// 1-> min     2-> max
PII vec[N][N]; // 1-> id    2->value
bool vis[N];
// 返回以x为起点,到T点的
PII V[N];
bool flag = 0;

PII dfs(int x) {

	if (vis[x])
		return V[x];

	vis[x] = 1;

	for (int i = 1; i <= vec[x][0].first; i ++) {
		int j = vec[x][i].first, w = vec[x][i].second;
		//	cout << x << " " << j << " " << w << endl;
		if (j == T) {
			flag = 1;
			V[x].first = min(V[x].first, w);
			V[x].second = max(V[x].second, w);
			//cout << x << " " << V[x].first << " " << V[x].second << "AAAA" << endl;
			continue;
		}
		V[x].first = min(min(w, dfs(j).first), V[x].first);
		V[x].second = max( max(w, dfs(j).second), V[x].second);
		//cout << x << " " << V[x].first << " " << V[x].second << "BBBB" << endl;
	}

	return V[x];

}


int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		V[i].first = 0x3f3f3f3f;
		V[i].second = -1;
	}

	for (int i = 1; i <= m; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		vec[a][0].first++;
		int t = vec[a][0].first;
		vec[a][t].first = b, vec[a][t].second = c;
		vec[b][0].first++;
		t = vec[b][0].first;
		vec[b][t].first = a, vec[b][t].second = c;
	}
	cin >> S >> T;
	PII res = dfs(S);
//	for (int i = 1; i <= 3; i++)
//		cout << V[i].first << " " << V[i].second << endl;
	if (!flag) {
		cout << "IMPOSSIBLE";
		return 0;
	}
	int a = res.second, b = res.first;
	int t = gcd(a, b);
	if (b / t == 1)  {
		cout << a / t;
	} else {
		cout << a / t << "/" << b / t;
	}
}

然而请你仔细考虑一下,我们如此操作得到的,只是
(全图唯一路径)一条路径的,最大值和最小值差值最大,而我们的初衷是每每找到一条路径,便比较,记录结果,然而由于记忆化的性质,我们得到答案其实是在起点,而并非终点,而起点,只会得到一次最终答案,所以无法进行多次比较与更改,不是我们想要的结果,所以,重新思考
再度思考
想得到比值最小,那就应该最小化差值(大比小),所以只需要枚举最小边,然后做克鲁斯卡尔算法,一旦连好终点,便是一个合法解,需要比较并替换的。
为什么要枚举呢?
我们只要连到起点和终点就可以了,所以有可能不需要连所有的点,枚举最小边就包含了所有情况,可以得到正确结果

#include <bits/stdc++.h>
using namespace std;
int n, m;
const int N = 10010;

struct node {
	int a, b, c;
};
node edge[N];
int fa[N];
int s, t;

bool cmp(node g, node h) {
	return g.c < h.c;
}

void init() {
	for (int i = 1; i <= n; i++)
		fa[i] = i;
}

int gcd(int a, int b) {
	return !b ? a : gcd(b, a % b);
}

int find(int x) {
	return fa[x] == x ? x : fa[x]=find(fa[x]);
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= m; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		edge[i].a = a, edge[i].b = b, edge[i].c = c;
	}
	cin >> s >> t;

	int x = 1, y = 0x3f3f3f3f;
	sort(edge + 1, edge + m + 1, cmp);
	bool f = 0;
	for (int i = 1; i <= m; i++) {
		//if(m-i+1<n-1) break;
		init();
		int nowy = -1;
		bool fg = 0;
		int nowx = edge[i].c;
		int u = 0;

		for (int j = i; j <= m; j++) {
			int l = edge[j].a, r = edge[j].b;
			
			l = find(l), r = find(r);
			if (l == r)
				continue;
			else {
				fa[l] = r;
				nowy = max(nowy, edge[j].c);
			}
			if (find(s) == find(t)) {
				f = 1;
				fg = 1;
				break;
			}
		}	
		if (fg) {			
			if (y * nowx > nowy * x) {
				int tt = gcd(nowx, nowy);
				y = nowy / tt, x = nowx / tt;
			}
		}

	}
	if (!f) {
		cout << "IMPOSSIBLE";
		return 0;
	}
	if (x == 1) {
		cout << y;
	} else {
		cout << y << "/" << x;
	}

}

P1772 [ZJOI2006] 物流运输

在这里插入图片描述
蓝题,不会

分析

题解写的很好
对于一个菜鸟来说,第一眼是加了限制的最短路 : 某些港口某些天会被封锁,其次,换路需要成本。
第一想法:日期是搜索状态,两种选择,换路/与不换路。
但是,请考虑:换路?怎么换?换哪条?最短路跑不了,换次短路?那万一次短路只能跑一天,还需要再度更换呢?换路的成本已经超过了换路带来的效益呢?
所以人家说显然,需要动态规划
但是,对于每天如此多的路径,而且又不知道前一天的路径是什么,你在第i天选择一条路径之后,是否需要加K
于是, 不妨合并一下路径,(i~j)天都可以走的最短路,让其变为一条路径,那dp答案f数组的状态,只需要考虑天数了,也就是第i天最少成本是多少
接下来, 预存一下,然后yxc的dp分析法可以得到答案

#include <bits/stdc++.h>
using namespace std;
int n, m, k, e;
typedef long long LL;

const int N = 110;
int ma[N][N];
LL f[N];
bool vis[N];
int dis[N][N];
int Dis[N];
typedef pair<int, int> PII;

int days[21][110];

bool check(int u, int x, int y) {
	for (int i = x; i <= y; i++) {
		if (days[u][i] == 0)
			return 0;
	}
	return 1;
}

void spfa(int x, int y) {

	memset(Dis, 0x3f, sizeof(Dis));
	memset(vis, 0, sizeof(vis));
	queue<int> q;
	q.push(1);
	vis[1] = 1;
	Dis[1] = 0;
	while (q.size()) {
		int t = q.front();
		q.pop();
		vis[t] = 0;
		for (int i = 1; i <= m; i++) {
			if (check(i, x, y)) {
				//cout << i << " " << x << " " << y << endl;
				if (Dis[i] > Dis[t] + ma[t][i]) {
					Dis[i] = Dis[t] + ma[t][i];
//					if (i == n)
//						cout << t << " " << Dis[t] << " " << t << " " << i << " " << ma[t][i] << "BBBBBBBBBBBBBBBBBBBB" << endl;
					if (!vis[i]) {
						vis[i] = 1;
						q.push(i);
					}
				}
			} else
				continue;
		}
	}
	dis[x][y] = Dis[m];
}


int main() {
	cin >> n >> m >> k >> e;
	memset(ma, 0x3f, sizeof(ma));
	for (int i = 1; i <= e; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		ma[a][b] = min(ma[a][b], c);
		ma[b][a] = min(ma[b][a], c);
	}
	//spfa  预存(i->j)的花费!
	int d;
	cin >> d;
	//cout << d << endl << endl;
	memset(days, 1, sizeof(days));
	for (int i = 1; i <= d; i++) {
		int p, l, r;
		cin >> p >> l >> r;
		for (int j = l; j <= r; j++) {
			days[p][j] = 0;
		}
	}
	//cout << "CCCCCCCCCCCCCCC" << ma[1][10] << endl;
//	cout << days[3].UN[2].first << " " << days[3].UN[2].second << "AAAA" << endl;
//	spfa(1, 1);
//	cout << dis[1][1] << endl;
	for (int i = 1; i <= n; i++) {
		for (int j = i; j <= n; j++) {
			spfa(i, j);
		}
	}
	//预存完毕,开始DP!

//	for (int i = 1; i <= n; i++) {
//		for (int j = i; j <= n; j++) {
//			cout << i << " " << j << " " << dis[i][j] << "AAAA";
//		}
//		cout << endl;
//	}
	memset(f, 0x3f, sizeof(f));
	f[1] = dis[1][1];
	f[0] = -k; // very important
	int lastday = 1; // last value;
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < i; j++) {
			//day : (i-j)
			f[i] = min(f[i], f[j] + (LL)dis[j + 1][i] * (i - j) + k);
		}
		//cout << f[i] << endl;
	}
	cout << f[n]  << endl;
	return 0;
}

有很多令人痛苦的细节问题 (LL)问题
m才是港口,而n不是港口

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值