求一个图最短路边的办法。好像下面的那个有问题。单向边和双向边一定是有区别的。这个比较容易。参照该文的最短路网络流题目和连通图题目一题求最短路关节边
另外上述2个题目的代码好像有问题。
在UVALIVE 6885中不能得到AC。不知道原因。感觉是对的。
另一种判断最短路的方法就是
从起点到u+从终点到v+边U,V权值==最短路值那么这条边为最短路
这种方案的代码
for (int i = 1 ; i <= M ; i++) {
scanf("%d%d%d",&u[i],&v[i],&w[i]);
u[i]++;v[i]++;
S.add_edge(u[i],v[i],w[i],i);
S.add_edge(v[i],u[i],w[i],i);
T.add_edge(u[i],v[i],w[i],i);
T.add_edge(v[i],u[i],w[i],i);
}
S.dijkstra(source);
T.dijkstra(target);
long long ans = 0;
for (int i = 1 ; i <= P ; i++) {
for (int j = S.head[i] ; j != -1 ; j = S.edge[j].next) {
int v = S.edge[j].v;
if (S.dis[i] + T.dis[v] + S.edge[j].w == S.dis[target])
//这条边为最短路边
}
}
同时另一种可能存在问题的方法如下
for (int i = 1 ; i <= m ; i++)
{
scanf("%d%d%I64d",&u[i],&v[i],&w[i]);
S.add_edge(u[i],v[i],w[i],i);
S.add_edge(v[i],u[i],w[i],i);
T.add_edge(u[i],v[i],w[i],i);
T.add_edge(v[i],u[i],w[i],i);
}
S.dijkstra(s);
T.dijkstra(t);
init();
for (int i = 1 ; i <= m ; i++)
{
int tu = u[i];
int tv = v[i];
LL tw = w[i];
if ((S.dis[tu] + tw == S.dis[tv] && T.dis[tv] + tw == T.dis[tu])
||(S.dis[tv] + tw == S.dis[tu] && T.dis[tu] + tw == T.dis[tv]))
{
add_edge(tu,tv,tw,i);
add_edge(tv,tu,tw,i);//这里是为了处理tarjan无向图 其实就是u[i],v[i],w[i]这条边为最短路边。
}
}
slove(n);
对于上述方案的有向图 建图有区别
如下
for (int i = 0 ; i < M ; i++)
{
scanf("%d%d%d",&u[i],&v[i],&w[i]);
St.add_edge(u[i],v[i],w[i],i + 1);
//St.add_edge(v[i],u[i],w[i],i + 1);
//Ted.add_edge(u[i],v[i],w[i],i + 1);
Ted.add_edge(v[i],u[i],w[i],i + 1);
}
scanf("%d%d",&A,&B);
St.dijkstra(A);
/* if (St.dis[B] == INF)
{
puts("0");
continue;
}
*/
Ted.dijkstra(B);
for (int i = 0 ; i < M ; i++)
{
int tu = u[i];
int tv = v[i];
int tw = w[i];
if (tu == tv) continue;
if ((St.dis[tu] + tw == St.dis[tv] && Ted.dis[tv] + tw == Ted.dis[tu])
||(St.dis[tv] + tw == St.dis[tu] && Ted.dis[tu] + tw == Ted.dis[tv]))
{
add_edge(tu,tv,1);//这里添加的是网络流的边无需双向
}
}
POJ 2387 Til the Cows Come Home
直接求最短路末班题。我这里是dijkstra+优先队列优化的班
#include
View Code
POJ 2253 Frogger
从1号点跳到2号点所经过的路径中最短的路径长度
可以用类似最小生成树的思路来解
这里用的kruskal
#include
View Code
另一种建立在理解dijkstra的基础上的代码。根据题意来确定相关更新
实际上这套题最困扰我的就是这几个dijkstra思想的题目。太弱。。
#include
View Code
POJ 1797 Heavy Transportation
一号点到N号点的合法路径中最小的边的最大值 ,这个实际就是最大生成树。直接kruskal很容易
这里还是要用下dijkstra和prim来区分一下。
下面这里是别人博客的一段话
在图论中,Prim算法是计算最小生成树的算法,而Dijkstra算法是计算最短路径的算法。二者看起来比较类似,因为假设全部顶点的集合是V,已经被挑选出来的点的集合是U,那么二者都是从集合V-U中不断的挑选权值最低的点加入U,那么二者是否等价呢?也就是说是否Dijkstra也可以计算出最小生成树而Prim也可以计算出从第一个顶点v0到其他点的最短路径呢?答案是否定的,否则就不必有两个算法了。
二者的不同之处在于“权值最低”的定义不同,Prim的“权值最低”是相对于U中的任意一点而言的,也就是把U中的点看成一个整体,每次寻找V-U中跟U的距离最小(也就是跟U中任意一点的距离最小)的一点加入U;而Dijkstra的“权值最低”是相对于v0而言的,也就是每次寻找V-U中跟v0的距离最小的一点加入U。
一个可以说明二者不等价的例子是有四个顶点(v0, v1, v2, v3)和四条边且边值定义为(v0, v1)=20, (v0, v2)=10, (v1, v3)=2, (v3, v2)=15的图,用Prim算法得到的最小生成树中v0跟v1是不直接相连的,也就是在最小生成树中v0v1的距离是v0->v2->v3->v1的距离是27,而用Dijkstra算法得到的v0v1的距离是20,也就是二者直接连线的长度。
第一份是dijkstra的
#include
View Code
第二份是prim的。仔细比对就发现差异
#include
View Code
POJ 3268 Silver Cow Party
有向图中从所有点到A点,再所有点从A点回到原本位置,问其中这些路径中最长的是哪个。
所有点从A回到原点是一次单元最短路即可。另一半怎么做。建反图,再从A求一次最短路,详情代码
#include
View Code
POJ 1860 Currency Exchange
如何判断正环。这里2分代码。第一份spfa
#include
View Code
第二份bellman_ford
注意迭代的次数为N-1,判断是否还正环和原本的一样、枚举边来松弛
#include
View Code
POJ 3259 Wormholes
判负环 同样2种代码
#include
View Code
#include
View Code
POJ 1502 MPI Maelstrom
阅读题,floyd做
#include
View Code
POJ 3660 Cow Contest
题意:给出了一系列比赛结果。比如其中一场比赛描述为A.B 说明A的能力值大于B。问最后有几个人的能力值的排名可以根据这几场比赛完全确定。floyd传递闭包
#include
View Code
POJ 2240 Arbitrage
字符串map处理一下,然后直接floyd处理即可
#include
View Code
POJ 1511 Invitation Cards
题目大意:给出n个点和n条有向边,求所有点到源点1的来回最短路之和(保证每个点都可以往返源点1)
#include
View Code
POJ 3159 Candies
差分约束+SPFA
给n个人派糖果,给出m组数据,每组数据包含A,B,c 三个数,
意思是A的糖果数比B少的个数不多于c,即B的糖果数 - A的糖果数<= c 。
最后求n 比 1 最多多多少糖果。
贴一下kuangbin神的题解吧
【解题思路】
这是一题典型的差分约束题。不妨将糖果数当作距离,把相差的最大糖果数看成有向边AB的权值,
我们得到 dis[B]-dis[A]<=w(A,B)。看到这里,我们联想到求最短路时的松弛技术,
即if(dis[B]>dis[A]+w(A,B), dis[B]=dis[A]+w(A,B)。
即是满足题中的条件dis[B]-dis[A]<=w(A,B),由于要使dis[B] 最大,
所以这题可以转化为最短路来求。
这题如果用SPFA 算法的话,则需要注意不能用spfa+queue 来求,会TLE ,而是用 spfa + stack
我自己直接dijkstra+优先队列是可以的。2个都贴一下
dijkstra
#include
View Code
spfa + stack
#include
View Code
POJ 2502 Subway
读完提按照他说的来建图即可
我记得当时做的时候感觉超级NM坑。我不知道我自己的建图哪里WA了。反正就换一种别人的写法救过了。我真的不服。。
#include <set>
#include
#include
#include
#include
#include
#include
#include
#include
#include <string>
#include
#include
#include
#include
#include
#include
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {
return a % b == 0 ? b : gcd(b, a % b);}
const int MAXN = 330;
const int MAXM = MAXN * MAXN;
const double walkv = 10 * 1000.0 / 60.0;
const double subv = 40 * 1000.0 / 60.0;
const double INF = 1e17;
const double eps = 1e-10;
struct Edge
{
int u,v,next;
double w;
bool walk;
};
struct heapnode
{
double val;
int u;
bool operator < (const heapnode & rhs) const
{
return val > rhs.val;
}
};
struct Dijkstra
{
int N,M;
bool done[MAXN];
int head[MAXN],tot;
Edge edge[MAXM];
priority_queueq;
double dis[MAXN];
void init(int n,int m)
{
N = n;
M = m;
memset(head,-1,sizeof(head));
tot = 0;
}
void add_edge(int u ,int v,double w,bool walk)
{
edge[tot].u = u;
edge[tot].v = v;
edge[tot].w = w;
edge[tot].next = head[u];
edge[tot].walk = walk;
head[u] = tot++;
}
void dijkstra(int s)
{
memset(done,false,sizeof(done));
while (!q.empty()) q.pop();
for (int i = 0 ; i <= N ; i++) dis[i] = INF;
dis[s] = 0.0;
q.push((heapnode){dis[s],s});
while (!q.empty())
{
heapnode x = q.top(); q.pop();
int u = x.u;
if (done[u])continue;
done[u] = true;
for (int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].v;
double tmp;
if (edge[i].walk)
tmp = dis[u] + edge[i].w / walkv ;
else tmp = dis[u] + edge[i].w / subv;
if (dis[v] > tmp)
{
dis[v] = tmp;
q.push((heapnode){dis[v],v});
}
}
}
}
}src;
struct point
{
double x,y;
int id;
}st,ed,res[MAXN];
double map[MAXN][MAXN];
bool sub[MAXN][MAXN];
int cas,tot;
double dis(point &a, point &b)
{
point p;
p.x = a.x - b.x;
p.y = a.y - b.y;
return sqrt(p.x * p.x + p.y * p.y);
}
int main()
{
// freopen("sample.txt","r",stdin);
src.init(MAXN,MAXM);
for (int i = 0 ; i < MAXN ; i++)
for (int j = 0 ; j < MAXN ; j++) map[i][j] = -1.0;
for (int i = 0 ; i < 2 ; i++) scanf("%lf%lf",&res[i].x,&res[i].y);
tot = 2;
point last,now;
last.x = last.y = -1;
while (scanf("%lf%lf", &now.x, &now.y) != EOF)
{
if (!(now.x == -1 && now.y == -1))
{
res[tot++] = now;
if (!(last.x == -1 && last.y == -1))
{
src.add_edge(tot - 1,tot - 2 ,dis(res[tot - 1],res[tot - 2]),false);
src.add_edge(tot - 2,tot - 1 ,dis(res[tot - 2],res[tot - 1]),false);
}
}
last = now;
}
for (int i = 0 ; i < tot ; i++)
for (int j = 0 ; j < tot ; j++)
if (map[i][j] == -1)
{
map[i][j] = dis(res[i],res[j]);
sub[i][j] = false;
src.add_edge(i,j,map[i][j],true);
src.add_edge(j,i,map[i][j],true);
}
src.dijkstra(1);
printf("%.0f\n",src.dis[0]);
return 0;
}
View Code
POJ 1062 昂贵的聘礼
读完提见图就行了
#include
View Code
POJ 1847 Tram
#include
View Code
题意:告诉n个点,每个点都有一个权值,A 到 B的时间等于 (val[b]-val[a])^3 都是单向边,可能存在负环,Q次询问,问从1到询问点的最短路,如果不可达或者点在负环上或者小于3,那么就输出? 否则输出最短路径值
标记负环上的点。
#include
View Code
HDU 4725 The Shortest Path in Nya Graph
总共3*N个点,1-N个点,N + 1 ---- N + N + N 这几个点分别是拆点的点。
至于为什么要拆点,注意到在一层的点不一定能直接到达。参考数据
3 0 1
1 1 1 ans = -1;
所有层拆点分为前向点 后项点。 见图为点连层前向点,层层之间后连前,具体看代码
#include
View Code
HDU 3416 Marriage Match IV
最短路的所有边只能走一次。直接最短路+最大流
这里唯一的问题就是最短路树是什么样子的。有向图和无向图的设置不太一样。前面连通图专题里有无向图的这里有向图的我相关的已经注释区分了
#include
View Code
HDU 4370 0 or 1
看bin神题解吧。此题太神了。
这里代码主要记录一下如何求闭环。循环队列等问题
复制的
转换思维的题啊,由一道让人不知如何下手的题,转换为了最短路 基本思路就是把矩阵看做一个图,图中有n个点,1号点出度为1, n号点入度为1,其它点出度和入度相等,路径长度都是非负数,
等价于一条从1号节点到n号节点的路径,故Xij=1表示需要经 过边(i,j),代价为Cij。Xij=0表示不经过边(i,j)。注意到Cij非负 且题目要求总代价最小,因此最优答案的路径一定可以对应一条简单路径。
最终,我们直接读入边权的邻接矩阵,跑一次1到n的最短路即可,记最短路为path。
漏如下的情况B: 从1出发,走一个环(至少经过1个点,即不能 是自环),回到1;从n出发,走一个环(同理),回到n。 也就是1和n点的出度和入度都为1,其它点的出度和入度为0.
由于边权非负,于是两个环对应着两个简单环。
因此我们可以从1出发,找一个最小花费环,记代价为c1, 再从n出发,找一个最小花费环,记代价为c2。 (只需在最短路算法更新权值时多加一条记录即可:if(i==S) cir=min(cir,dis[u]+g[u][i]))
故最终答案为min(path,c1+c2) */ /* 本程序用SPFA来完成最短路。 但是由于要计算从出发点出发的闭环的路径长度。 所以要在普通SPFA的基础上做点变化。
就是把dist[start]设为INF。同时一开始并不是让出发点入队,而是让 出发点能够到达的点入队。
代码:
#include
View Code
POJ 3169 Layout
查分约束变换一下不等式就行了
对那ML个条件
d[y] - d[x] <= w -> d[x] + w >= d[y]
对那MD个条件
d[y] - d[x] >= w -> d[y] + (-w) >= d[x]
对于一个不等式 d[u] + w >= d[v] 我们可以加边(u, v , w) 因为松弛操作
2份代码
#include
View Code
#include
View Code