来的十分之晚,可能是重复做一件事情太过疲惫,效率下降很多,心理上的驱动力也下降许多,希望尽快调整过来
分析题目,写代码很快,五分之四的时间都用来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不是港口