RoadBlocks
参考博文:挑战程序设计竞赛: Roadblocks
参考博文:【dijkstra优化/次短路径】POJ3255-Roadblocks
- 思路:dijstra算法的变体。
- 这道题的做法和最短路径基本一致,唯一的不同点在于,在求出最短路径的情况下必须要保留下次短路径。对于Dijkstra判断中取出的每一个点,如果到它的最短距离大于当前该点的次短距离,则当前该点已经取到最短距离和次短距离,不进行操作,否则进行两次判断:如果小于最短边,则赋给最短变,并将最短边赋给次短边;或者如果大于最短变且小于次短边,则赋给次短边。两次完成之后均要加入队列。
- 并且没有使用vis数组来标记已经求出最短路径的结点,因为还需要多次使用该结点求次短路径,因此需要遍历所有边。
代码:
#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
/*
不使用vis来标记是否访问过,是因为可以访问多次来取次短路径
*/
const int MAX = 50005;
const int INF = 1<<29;
struct Node
{
int v, c;//编号和最短路径距离
Node(int v=0, int c=0): v(v), c(c){}
bool operator < (const Node& a) const{
return c>a.c;
}
};
int d[MAX], d2[MAX];//最短路和次短路
vector<Node> G[MAX];//邻接表
priority_queue<Node> pq;
Node pn;
int N, R, A, B, D;
void dijstra(int start)
{
//初始化
fill(d, d+N+1, INF);
fill(d2, d2+N+1, INF);
d[start] = 0;
pq.push(Node(start, d[start]));
while(!pq.empty())
{
pn = pq.top(); pq.pop();
int u = pn.v, dis = pn.c;
if(d2[u]<dis) continue;//如果次短路径都小于dis 那么就不用再继续去更新
for(int j=0; j<G[u].size(); j++)//遍历邻接表
{
Node n = G[u][j];
int dis2 = dis+n.c, v = n.v;
//更新最短路
if(d[v]>dis2)
{
swap(d[v], dis2);
pq.push(Node(v, d[v]));
}
if(d2[v]>dis2 && d[v]<dis2)
{
d2[v] = dis2;
pq.push(Node(v, d2[v]));
}
}
}
printf("%d\n", d2[N]);
}
int main()
{
while(scanf("%d%d", &N, &R)!=EOF)
{
for(int i=0; i<R; i++)
{
cin >> A >> B >> D;
G[A].push_back(Node(B, D));
G[B].push_back(Node(A, D));
}
dijstra(1);
}
return 0;
}
Conscription
参考博文:挑战程序设计竞赛:Conscription
题目大意:选择有N个女兵和M个男兵,每选一个花费10000,男女兵之间有关系d,可以将花费降低d,问按什么顺序选择花费最少。
- 思路:可以转换为求解最小生成树(MST),根据题意知使用Kruskal方法比较好(使用并查集)。注意在找出MST后,计算其花费后,需要统计有多少个连通子图(单点—与其他点没有任何联系,也可以看做一个连通子图),然后加上其总数*10000。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 50005;
int N, M, R, T, x, y, d;
struct Edge
{
int from, to, value;
bool operator < (const Edge &A) const
{
return value < A.value;
}
};
Edge G[MAX];
int pre[MAX], Rank[MAX], vis[MAX];
void Init(int n)
{
for(int i=0; i<=n; i++)
{
pre[i] = i;
Rank[MAX] = 0;
}
}
int findRoot(int x)
{
int r = x;
while(r!=pre[r]) r = pre[r];
int j = x, i;
while(j!=pre[j])
{
i = pre[j];
pre[j] = r;
j = i;
}
return r;
}
void join(int x, int y)
{
int fx = findRoot(x), fy = findRoot(y);
if(Rank[fx]>Rank[fy])
pre[fy] = fx;
else
{
pre[fx] = fy;
if(Rank[fx]==Rank[fy]) Rank[fy]++;
}
}
bool same(int x, int y)
{
return findRoot(x)==findRoot(y);
}
void solve()
{
int ans = 0;
for(int i=0; i<R; i++)
{
if(!same(G[i].from, G[i].to))
{
ans += G[i].value;
join(G[i].from, G[i].to);
}
}
//找出有多少个连通图(包括单点)
for(int i=0; i<N+M; i++)
{
if(pre[i]==i)
{
ans += 10000;
}
}
printf("%d\n", ans);
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d%d", &N, &M, &R);
Init(N+M);
for(int i=0; i<R; i++)
{
scanf("%d%d%d", &x, &y, &d);
G[i].from = x, G[i].to = y+N, G[i].value = 10000-d;
}
sort(G, G+R);
solve();
}
return 0;
}
Layout
题目链接Layout
参考博文:Layout POJ NO.3169
Layout POJ - 3169 (差分约束+最短路)
挑战程序设计竞赛: Layout讲解了为什么将不等式转换为图结构。
思路:重点是建图思想,并将问题转换为求带负环的最短路问题。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAX = 100000;
const int INF = 1<<29;
struct Edge
{
int from, to, cost;
};
int N, ML, MD, A, B, D, E;
Edge e[MAX];
LL d[MAX];
//找到各个结点的最短路径,并判断是否存在负环
bool Bellman_Ford()
{
fill(d, d+MAX, INF);
d[1] = 0;
int k = 0;
while(1)
{
bool updated = false;
for(int i=0; i<E; i++)//遍历所有的边
{
Edge w = e[i];
if(d[w.from]!=INF && d[w.to]>d[w.from]+w.cost)
{
d[w.to] = d[w.from]+w.cost;
updated = true;
}
}
if(!updated) break;//没有更新则立即停止
k++;
if(k>=N) return false;//判断是否有负环
}
return true;
}
//单纯判断是否存在负环
bool find_negative_loop()
{
//memset(d, 0, sizeof(d));
fill(d, d+MAX, INF);
d[1] = 0;
for(int i=1; i<=N; i++)
{
for(int j=0; j<E; j++)
{
Edge w = e[j];
if(d[w.to]>d[w.from]+w.cost)
{
d[w.to]=d[w.from]+w.cost;
if(i==N) return true;//第N次仍然更新
}
}
}
return false;
}
int main()
{
E = 0;
scanf("%d%d%d", &N, &ML, &MD);
for(int i=0; i<ML; i++)
{
scanf("%d%d%d", &A, &B, &D);
e[E].from = A, e[E].to = B, e[E++].cost = D;
}
for(int i=0; i<MD; i++)
{
scanf("%d%d%d", &A, &B, &D);
e[E].from = B, e[E].to = A, e[E++].cost = -D;
}
/*
if(find_negative_loop())
{
printf("-1\n");
return 0;
}*/
if(!Bellman_Ford())
printf("-1\n");
else if(d[N]==INF)
printf("-2\n");
else
printf("%lld\n", d[N]);
return 0;
}