2016-2017 ACM-ICPC Northwestern European Regional Programming Contest (NWERC 2016)
Problem I Iron and Coal
题目链接:http://codeforces.com/gym/101170/attachments
There are many excellent strategy board games, and your favourite among them is called “Steel Age”. It offers many different paths to victory but you prefer the blood-and-fire-strategy: build as many soldiers as possible and club your opposition into submission. To be able to build soldiers you need two resources: iron ore and coal.
Description
在 n 个点里,每个点只有没有资源、有 1 号资源和有 2 号资源这三种状态,从 1 这个点出发,寻找一个 1 号资源和一个 2 号资源的最短路,需要注意的是对于走过的重复路径只计算一次。
Solution
值得注意的是:重复路径只计算一次,这就意味着有两种路径,第一种是在不会出现路径重复的情况下拿到两种资源,第二种是从起点走到一个岔路口 p,然后分别从p走到一个 1 号资源和一个 2 号资源(因为重复路径只计算一次)。
那么问题就转化为了在 n 个中寻找一个点 p,使得点 p 可以通到起点、一个 1 号资源和一个 2 号资源,那么答案就是这三个距离之和!
首先利用单源最短路径算法 SPFA 计算出起点到所有点的最短距离为 cost[i],然后反向建图,利用 BFS 找出每个点到最近的 1 号资源和 2 号资源的距离(全部资源同时当起点开始bfs),然后遍历所有点,把这 3 个距离取和求最小值。
也可以直接三次 BFS,代码量少又直观一些:
- 第一遍BFS:从1号点BFS一遍整个正向边的图,记录数组 dist[0][i] 为每个点距离1号点的距离。
- 第二遍BFS:从每个铁矿BFS一遍整个反向边的图,记录数组dist[1][i]为每个点距离每个铁矿的最近距离。
- 第三遍BFS:从每个煤BFS一遍整个反向边的图,和第二遍类似。
对于每个点,三个 dist 数组求和就是从一号点到最近的 1 号资源和最近的 2 号资源距离和。
Code
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cstdlib>
using namespace std;
const int MaxN = 1e5 + 5;
const int INF = 0x3f3f3f3f;
struct node {
int x, t;
}S;
vector<int> G[MaxN], re_G[MaxN];
vector<int> loc[3];
int col[MaxN], iro[MaxN];
int vis[MaxN], cost[MaxN];
int val[MaxN][3];
int n, m, k;
void SPFA(){
S.x = 1, S.t = 0;
queue<node> que;
que.push(S);
vis[1] = true;
memset(cost, INF, sizeof(cost));
cost[1] = 0;
while(!que.empty()){
node now = que.front(), nxt;
que.pop();
for(int i = 0; i < G[now.x].size(); i++) {
int v = G[now.x][i];
nxt.x = v; nxt.t = now.t + 1;
if(nxt.t < cost[nxt.x]) {
cost[nxt.x] = nxt.t;
if(!vis[nxt.x]) {
que.push(nxt);
vis[nxt.x] = true;
}
}
}
vis[now.x] = false;
}
}
void BFS()
{
memset(val, 0x3f3f3f3f, sizeof(val));
for(int i = 1; i <= n; i++) {
if(col[i]) loc[1].push_back(i), val[i][1] = 0; //1号资源的所有位置
if(iro[i]) loc[2].push_back(i), val[i][2] = 0; //2号资源的所有位置
}
for(int i = 1; i <= 2; i++) {
for(int j = 0; j < loc[i].size(); j++) {
int now = loc[i][j];
for(int k = 0; k < re_G[now].size(); k++) {
int u = re_G[now][k];
int c = val[now][i] + 1;
if(val[u][i] == INF) val[u][i] = c, loc[i].push_back(u);
}
}
}
}
int main()
{
scanf("%d %d %d", &n, &m, &k);
for(int i = 1; i <= m; i++) {
int x; scanf("%d", &x);
col[x] = 1;
}
for(int i = 1; i <= k; i++) {
int x; scanf("%d", &x);
iro[x] = 1;
}
for(int i = 1; i <= n; i++) {
int t; scanf("%d", &t);
while(t--) {
int x; scanf("%d", &x);
G[i].push_back(x);
re_G[x].push_back(i);
}
}
SPFA();
BFS();
int ans = INF;
for(int i = 1; i <= n; i++)
if(cost[i] != INF && val[i][1] != INF && val[i][2] != INF)
ans = min(ans, cost[i] + val[i][1] + val[i][2]);
if(ans == INF) printf("impossible\n");
else printf("%d\n", ans);
return 0;
}
对于三次 BFS 的代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cstdlib>
using namespace std;
const int MaxN = 2e5 + 5;
const int INF = 100000000;
//这里赋值为0x3f3f3f3f就会玄学wa22,不知道为什么....希望有大佬指点
vector<int> G[MaxN], re_G[MaxN];
int s[] = {0, 1}, col[MaxN], iro[MaxN];
int dist[3][MaxN];
void BFS(int id, int *src, int num, vector<int> *vec)
{
queue<int> que;
for(int i = 0; i < MaxN; i++) dist[id][i] = INF;
for(int i = 1; i <= num; i++) {
dist[id][src[i]] = 0;
que.push(src[i]);
}
while(!que.empty()) {
int now = que.front(); que.pop();
for(int i = 0; i < vec[now].size(); i++) {
int nxt = vec[now][i];
if(dist[id][nxt] == INF) {
dist[id][nxt] = dist[id][now] + 1;
que.push(nxt);
}
}
}
}
int main()
{
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
for(int i = 1; i <= m; i++) scanf("%d", &col[i]);
for(int i = 1; i <= k; i++) scanf("%d", &iro[i]);
for(int i = 1; i <= n; i++) {
int t; scanf("%d", &t);
while(t--) {
int x; scanf("%d", &x);
G[i].push_back(x);
re_G[x].push_back(i);
}
}
BFS(0, s, 1, G);
BFS(1, col, m, re_G);
BFS(2, iro, k, re_G);
int ans = INF;
for(int i = 1; i <= n; i++)
ans = min(ans, dist[0][i] + dist[1][i] + dist[2][i]);
if(ans == INF) printf("impossible\n");
else printf("%d\n", ans);
return 0;
}