Til the Cows Come Home
题意为问 N N N 到 1 1 1 的最短距离,直接套模板求单源最短路就好
#include <cstdio>
#include <algorithm>
#include <queue>
#include <climits>
const int MAXN = 1000;
const int MAXM = 2000;
std::queue<int> q;
struct Edge {
int to, next, w;
} edge[MAXM << 1 + 2];
int tot, n, m;
int pre[MAXN], dis[MAXN];
void addEdge(int u, int v, int w) {
tot++;
edge[tot].w = w;
edge[tot].to = v;
edge[tot].next = pre[u];
pre[u] = tot;
}
void init() {
for (int i = 1; i <= n; i++)
dis[i] = INT_MAX;
dis[1] = 0;
q.push(1);
}
void spfa() {
while (!q.empty()) {
int tmp = q.front(); q.pop();
for (int i = pre[tmp]; i; i = edge[i].next) {
if (dis[edge[i].to] > dis[tmp] + edge[i].w) {
dis[edge[i].to] = dis[tmp] + edge[i].w;
q.push(edge[i].to);
}
}
}
}
int main()
{
scanf("%d %d", &m, &n);
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addEdge(u, v, w);
addEdge(v, u, w);
}
init();
spfa();
printf("%d\n", dis[n]);
return 0;
}
Frogger
题意是求两个青蛙之间最大路径的最小值,可以用Kruskal算法解决。
设某一条边为
x
x
x,当
x
x
x 加入后使得两个青蛙之间由不连通变为联通,那么
x
x
x 就为最小瓶颈边,证明的话,因为边是从小到大加的,所以很显然。。。
#include <cstdio>
#include <cmath>
#include <climits>
#include <algorithm>
const int MAXN = 200;
const int MAXM = 40000;
struct Union_Find {
int dad[MAXN + 2];
int n;
void init() {
for (int i = 1; i <= n; i++)
dad[i] = i;
}
Union_Find(int n) {
this->n = n;
init();
}
int find(int x) {
if (dad[x] != x) dad[x] = find(dad[x]);
return dad[x];
}
void merge(int x, int y) {
dad[find(x)] = find(y);
}
bool judge(int x, int y) {
return find(x) == find(y);
}
};
struct Point {
int x, y;
} point[MAXN + 2];
struct Edge {
int x, y;
double w;
bool operator < (const Edge &a) const {return w < a.w;}
} edge[MAXM + 2];
int n, cnt, turn;
double ans;
void init() {
cnt = 0; ans = INT_MAX;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++) {
double dis = sqrt((point[i].x - point[j].x) * (point[i].x - point[j].x) + (point[i].y - point[j].y) * (point[i].y - point[j].y));
cnt++;
edge[cnt].x = i, edge[cnt].y = j, edge[cnt].w = dis;
}
}
int main()
{
while (scanf("%d", &n) != EOF) {
if (n == 0) return 0;
turn++;
for (int i = 1; i <= n; i++) {
scanf("%d %d", &point[i].x, &point[i].y);
}
init();
Union_Find bcj(n);
std::sort(edge + 1, edge + 1 + cnt);
for (int i = 1; i <= cnt; i++) {
bcj.merge(edge[i].x, edge[i].y);
if (bcj.judge(1, 2)) {
ans = edge[i].w;
break;
}
}
printf("Scenario #%d\nFrog Distance = %.3f\n\n", turn, ans);
}
return 0;
}
Heavy Transportation
这个题意和上面那道题刚好反过来,是求最小路径的最大值,所以从大到小枚举加边,用Kruskal跑一下就好了
#include <cstdio>
#include <algorithm>
const int MAXN = 1000;
struct Edge {
int x, y, w;
bool operator < (const Edge &a) const {return w > a.w;}
} edge[1000000 + 2];
struct Union_Find {
int dad[MAXN];
int n;
void init() {
for (int i = 1; i <= n; i++)
dad[i] = i;
}
Union_Find(int n) {
this->n = n;
init();
}
int find(int x) {
if (dad[x] != x) dad[x] = find(dad[x]);
return dad[x];
}
void merge(int x, int y) {
dad[find(x)] = find(y);
}
bool judge(int x, int y) {
return find(x) == find(y);
}
};
int T, n, m, ans, rnd;
int main()
{
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &edge[i].x, &edge[i].y, &edge[i].w);
}
std::sort(edge + 1, edge + 1 + m);
Union_Find b(n);
for (int i = 1; i <= m; i++) {
b.merge(edge[i].x, edge[i].y);
if (b.judge(1, n)) {
ans = edge[i].w;
break;
}
}
printf("Scenario #%d:\n%d\n\n", ++rnd, ans);
}
return 0;
}
Silver Cow Party
题意是给了
N
N
N 头牛,每头牛都要到达一个目标牛那里然后返回,求每头牛的最短路径的最大值。
从目标牛处返回很好计算,只要以目标牛为源点跑一边单源最短路即可,然后从每头牛到达目标牛的计算方法是把边取反,再以目标牛为源点跑一边单源最短路,然后把每头牛的两个
d
i
s
dis
dis 值加起来求一个最大值即可。
#include <cstdio>
#include <climits>
#include <queue>
#include <algorithm>
const int MAXN = 1000 + 2;
const int MAXM = 100000 + 2;
std::queue<int> q;
struct Edge {
int to, w, next;
} edge[MAXM], edge2[MAXM];
int n, m, x, tot, tot2, ans;
int pre[MAXN], pre2[MAXN], dis[MAXN], dis2[MAXN];
bool in[MAXN], in2[MAXN];
void addEdge(int u, int v, int w) {
tot++;
edge[tot].to = v;
edge[tot].w = w;
edge[tot].next = pre[u];
pre[u] = tot;
}
void addEdge2(int u, int v, int w) {
tot2++;
edge2[tot2].to = v;
edge2[tot2].w = w;
edge2[tot2].next = pre2[u];
pre2[u] = tot2;
}
void spfa(int s) {
for (int i = 1; i <= n; i++)
dis[i] = INT_MAX;
while(!q.empty()) q.pop();
dis[s] = 0;
q.push(s);
while(!q.empty()) {
int tmp = q.front(); q.pop(); in[tmp] = false;
for (int i = pre[tmp]; i; i = edge[i].next) {
if (dis[edge[i].to] > dis[tmp] + edge[i].w) {
dis[edge[i].to] = dis[tmp] + edge[i].w;
if (!in[edge[i].to]) {
q.push(edge[i].to);
in[edge[i].to] = true;
}
}
}
}
}
void spfa2(int s) {
for (int i = 1; i <= n; i++)
dis2[i] = INT_MAX;
while(!q.empty()) q.pop();
dis2[s] = 0;
q.push(s);
while(!q.empty()) {
int tmp = q.front(); q.pop(); in2[tmp] = false;
for (int i = pre2[tmp]; i; i = edge2[i].next) {
if (dis2[edge2[i].to] > dis2[tmp] + edge2[i].w) {
dis2[edge2[i].to] = dis2[tmp] + edge2[i].w;
if (!in2[edge2[i].to]) {
q.push(edge2[i].to);
in2[edge2[i].to] = true;
}
}
}
}
}
int main()
{
scanf("%d %d %d", &n, &m, &x);
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addEdge(u, v, w);
addEdge2(v, u, w);
}
spfa(x); spfa2(x);
for (int i = 1; i <= n; i++)
ans = std::max(ans, dis[i] + dis2[i]);
printf("%d\n", ans);
return 0;
}
Currency Exchange
找正权回路,用 spfa,当一个点重复入队超过 n n n 次时,即存在正权回路。(需保证在队列里的点不能再次入队,即队列中不能同时存在多个同一个点)
#include <cstdio>
#include <algorithm>
#include <queue>
#include <climits>
const int MAXN = 100 + 2;
std::queue<int> q;
struct Edge {
int to, next;
double r, c;
} edge[MAXN << 1];
int n, m, s, tot;
int pre[MAXN], num[MAXN];
double v;
double dis[MAXN];
bool in[MAXN];
void addEdge(int u, int v, double r, double c) {
tot++;
edge[tot].to = v;
edge[tot].r = r;
edge[tot].c = c;
edge[tot].next = pre[u];
pre[u] = tot;
}
bool spfa() {
while (!q.empty()) q.pop();
dis[s] = v;
q.push(s);
in[s] = true;
num[s]++;
while (!q.empty()) {
int tmp = q.front(); q.pop();
for (int i = pre[tmp]; i; i = edge[i].next) {
if (dis[edge[i].to] < (dis[tmp] - edge[i].c) * edge[i].r) {
dis[edge[i].to] = (dis[tmp] - edge[i].c) * edge[i].r;
if (!in[edge[i].to]) {
in[edge[i].to];
q.push(edge[i].to);
num[edge[i].to]++;
if (num[edge[i].to] > n) return true;
}
}
}
}
if (dis[s] > v) return true;
return false;
}
int main()
{
scanf("%d %d %d %lf", &n, &m, &s, &v);
for (int i = 1; i <= m; i++) {
int u, v;
double r1, c1, r2, c2;
scanf("%d %d %lf %lf %lf %lf", &u, &v, &r1, &c1, &r2, &c2);
addEdge(u, v, r1, c1);
addEdge(v, u, r2, c2);
}
if (spfa()) puts("YES");
else puts("NO");
}
Wormholes
虫洞可以回到过去,相当于负权值,所以找一下存不存在负权回路即可。
#include <cstdio>
#include <cstring>
#include <queue>
#include <climits>
#include <algorithm>
const int MAXN = 50000 + 2;
const int MAXM = 25000 + 2;
struct Edge {
int to, w, next;
} edge[MAXM << 1];
int n, m, w, T, tot;
int dis[MAXN], pre[MAXN], num[MAXN];
bool in[MAXN];
void init() {
tot = 0;
memset(pre, 0, sizeof(pre));
}
void addEdge(int u, int v, int w) {
tot++;
edge[tot].to = v;
edge[tot].w = w;
edge[tot].next = pre[u];
pre[u] = tot;
}
bool spfa(int s) {
std::queue<int> q;
memset(num, 0, sizeof(num));
memset(in, 0, sizeof(in));
for (int i = 1; i <= n; i++)
dis[i] = INT_MAX;
dis[s] = 0;
in[s] = true;
num[s]++;
q.push(s);
while (!q.empty()) {
int tmp = q.front(); q.pop(); in[tmp] = false;
for (int i = pre[tmp]; i; i = edge[i].next) {
if (dis[edge[i].to] > dis[tmp] + edge[i].w) {
dis[edge[i].to] = dis[tmp] + edge[i].w;
if (!in[edge[i].to]) {
in[edge[i].to] = true;
num[edge[i].to]++;
if (num[edge[i].to] > n) return true;
q.push(edge[i].to);
}
}
}
}
return false;
}
int main()
{
scanf("%d", &T);
while (T--) {
scanf("%d %d %d", &n, &m, &w);
init();
for (int i = 1; i <= m; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addEdge(u, v, w);
addEdge(v, u, w);
}
for (int i = 1; i <= w; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
addEdge(u, v, -w);
}
spfa(1) ? puts("YES") : puts("NO");
}
return 0;
}
MPI Maelstrom
题意就是给你 n n n 个点,求从 1 1 1 到其他 n − 1 n-1 n−1 个点之间最短路的最大值,直接套模板就好,需要注意的就是可以用 a t o i ( ) atoi() atoi() 函数来把字符串转化为数字,很方便
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <queue>
const int MAXN = 100 + 5;
struct Edge {
int w, to, next;
} edge[MAXN * MAXN];
int n, tot, ans = INT_MIN;
int dis[MAXN], pre[MAXN];
void addEdge(int u, int v, int w) {
tot++;
edge[tot].w = w;
edge[tot].to = v;
edge[tot].next = pre[u];
pre[u] = tot;
}
void spfa() {
for (int i = 1; i <= n; i++)
dis[i] = INT_MAX;
dis[1] = 0;
std::queue<int> q;
q.push(1);
while(!q.empty()) {
int tmp = q.front(); q.pop();
for (int i = pre[tmp]; i; i = edge[i].next) {
if (dis[edge[i].to] > dis[tmp] + edge[i].w) {
dis[edge[i].to] = dis[tmp] + edge[i].w;
q.push(edge[i].to);
}
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; i++)
for (int j = 1; j < i; j++) {
char s[10];
scanf("%s", s);
if (s[0] != 'x') {
addEdge(i, j, atoi(s));
addEdge(j, i, atoi(s));
}
}
spfa();
for (int i = 2; i <= n; i++)
ans = std::max(ans, dis[i]);
printf("%d\n", ans);
return 0;
}
Cow Contest
题意为给你一些牛之间的
r
a
n
k
rank
rank 关系,问最多能确定多少头牛的
r
a
n
k
rank
rank 值
当一头牛有
n
−
1
n-1
n−1 个
r
a
n
k
rank
rank 关系时,这头牛的
r
a
n
k
rank
rank 是可以确定的,所以只要确定每头牛与其他牛之间的
r
a
n
k
rank
rank 关系就行,如果把这种关系看成有向边的话,就可以转化到用
f
l
o
y
d
floyd
floyd 判断两点间是否联通
#include <cstdio>
const int MAXN = 100 + 2;
int n, m, ans;
int mp[MAXN][MAXN];
void floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
if (!mp[i][j]) mp[i][j] = mp[i][k] && mp[k][j];
}
}
int main()
{
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d %d", &u, &v);
mp[u][v] = 1;
}
floyd();
for (int i = 1; i <= n; i++) {
int sum = 0;
for (int j = 1; j <= n; j++) {
if (i == j) continue;
if (mp[i][j] || mp[j][i]) sum++;
}
if (sum == n - 1) ans++;
}
printf("%d\n", ans);
return 0;
}