DAG : Directed Acyclic Graph 无回路有向图
你一直困惑的原因是你忽略了dijkstra算法的思想每次利用优先队列找出d值最小的节点然后从该节点出发更新该节点边上的节点这样的话 如果d值小于p.first 说明之前的操作中已经该节点已经被更新,而且更新之后的节点存在于后面的队列中 故此处无需考虑 如果d值不小于p.first 说明d值等于p.fist 即表明之前的操作中没有被更新,故此处可以进行对与其相邻元素的更新工作
struct edge {int to, cost; };
typedef pair<int , int> P;///first 是最短距离 second 是顶点编号
vector G[maxn];
int d[maxn];///到某个顶点的最短距离
void dijkstra(int s)
{
priority_queuevector
, greater
>que;
fill(d, d+N, INF);
d[s] = 0;
que.push(P(0 , s));
while(!que.empty())
{
P p = que.top();
que.pop();
int v = p.second;
if(d[v] < p.first) continue;///否则就要更新与p.second接壤的节点
for(int i=0; iif(d[e.to] > d[v] + e.cost)
{
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
struct edge {int to, cost; };
typedef pair<int,int> P;
int V;
vector G[maxn];
int d[maxn];//最短路
int d2[maxn];//次短路
void dijkstra(int s)
{
priority_queuevector
,greater
>q;
for(int i=0; i0;
q.push(P(d[s],s));
while(!q.empty())
{
P p = q.top();
q.pop();
int v = p.second,c=p.first;
if(d2[v]continue;
for(int i=0; iint c2 = c+e.w;//新的权值
if(d[e.to]>c2) //维护最短路
{
swap(d[e.to],c2);
q.push(P(d[e.to],e.to));
}
if(d2[e.to]>c2&&d[e.to]//维护次短路
{
d2[e.to]=c2;
q.push(P(d2[e.to],e.to));
}
}
}
}
int n;
int d[maxn][maxn];
void Floyd()
{
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
d[i][j] = (i==j ? 0 : INF);
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
}
struct Edge
{
int v, w, next;
}edge[maxn*20]//边数根据具体情况而定
int n, m, tot;
int dist[maxn], head[maxn];
bool vis[maxn];
void add_edge(int u, int v, int w)
{
edge[tot].v = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot++;
}
void SPFA(int s)
{
memset(vis, false, (n+2)*sizeof(bool));
for(int i=1; i<=n; i++) dist[i] = INF;
vis[s] = true;
dist[s] = 0;
queue<int> Q;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;//重要点一
for(int j=head[u]; j!=-1; j=edge[j].next)
{
int v = edge[j].v;
int w = edge[j].w;
if(dist[u] < INF && dist[u] + w < dist[v])
{
dist[v] = dist[u] + w;
if(!vis[v])
{
vis[v] = true;//重要点二
Q.push(v);
}
}
}
}
}
#include
using namespace std;
#define maxn 1000
vector<int>G[maxn];
bool vis[maxn];
int link[maxn];
int n, m;
bool Find(int u)
{
for(int i=0; iint &v = G[u][i];
if(!vis[v])
{
vis[v] = true;
if(link[v]==-1 || Find(link[v]))
{
link[v] = u;
link[u] = v;//这个地方要注意,无向图,区别于两组对象
return true;
}
}
}
return false;
}
void solve()
{
int ans = 0;
for(int i=1; i<=n; i++)
if(link[i]==-1)///这里需要加上判断 否则对已经匹配好的进行重新匹配,造成错误结果
{
memset(vis, 0, sizeof(vis));
if(Find(i)) ans++;
}
printf("%d\n", ans);
}
const int N = 310;
const int INF = 0x3f3f3f3f;
int nx,ny;//两边的点数
int g[N][N];//二分图描述 坐标点为 0 ~ n - 1
int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
int slack[N];
bool visx[N],visy[N];
bool DFS(int x)
{
visx[x] = true;
for(int y = 0; y < ny; y++)
{
if(visy[y])continue;
int tmp = lx[x] + ly[y] - g[x][y];
if(tmp == 0)
{
visy[y] = true;
if(linker[y] == -1 || DFS(linker[y]))
{
linker[y] = x;
return true;
}
}
else if(slack[y] > tmp)
slack[y] = tmp;
}
return false;
}
int KM()
{
memset(linker,-1,sizeof(linker));
memset(ly,0,sizeof(ly));
for(int i = 0; i < nx; i++)
{
lx[i] = -INF;
for(int j = 0; j < ny; j++)
if(g[i][j] > lx[i])
lx[i] = g[i][j];
}
for(int x = 0; x < nx; x++)
{
for(int i = 0; i < ny; i++)
slack[i] = INF;
while(true)
{
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(DFS(x))break;
int d = INF;
for(int i = 0; i < ny; i++)
if(!visy[i] && d > slack[i])
d = slack[i];
for(int i = 0; i < nx; i++)
if(visx[i])
lx[i] -= d;
for(int i = 0; i < ny; i++)
{
if(visy[i])ly[i] += d;
else slack[i] -= d;
}
}
}
int res = 0;
for(int i = 0; i < ny; i++)
if(linker[i] != -1)
res += g[linker[i]][i];
return res;
}
详细讲解
原理:在残量网络上不断贪心的进行增广(最小割最大流)
struct Edge
{
int from, to, cap, flow;
Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f){}
};
int n, m;
vector edges;
vector<int> G[maxn];
int a[maxn];
int p[maxn];
void init()
{
for(int i=0; ivoid AddEdge(int from, int to, int cap)
{
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int Maxflow(int s, int t)
{
int flow = 0;
for(;;)
{
memset(a, 0, sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = INF;
while(!Q.empty())
{
int x = Q.front(); Q.pop();
for(int i=0; iif(!a[e.to] && e.cap > e.flow)
{
p[e.to] = G[x][i];
a[e.to] = min(a[x], e.cap - e.flow);
Q.push(e.to);
}
}
if(a[t]) break;
}
if(!a[t]) break;
for(int u=t; u!=s; u=edges[p[u]].from)
{
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
测试题 HDU 1532
struct Edge
{
int u, v, c, next;
}e[E<<2];
int n, m, s, t;
int d[V], head[V], cnt;//注意head在主函数的需要初始化
void adde(int u, int v, int c)//添加一条有向边,如果是无向边需添加两次
{
e[cnt].u = u;
e[cnt].v = v;
e[cnt].c = c;
e[cnt].next = head[u];
head[u] = cnt++;
e[cnt].u = v;
e[cnt].v = u;
e[cnt].c = 0;
e[cnt].next = head[v];
head[v] = cnt++;
}
int bfs()//构造层次图
{
memset(d, -1, (n+3)*sizeof(int));
d[s] = 0;
queue<int> Q;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
for(int i=head[u]; i!=-1; i=e[i].next)
{
int v = e[i].v;
if(d[v]==-1 && e[i].c>0)
{
d[v] = d[u] + 1;
Q.push(v);
}
}
}
return d[t] != -1;
}
int dfs(int x, int a)//寻找最短增广路
{
if(x==t || a==0) return a;
int flow = 0, f;
for(int i=head[x]; i!=-1 && flowint v = e[i].v;
if(d[v]==d[x]+1 && e[i].c>0)
{
int t = min(e[i].c, a-flow);
f = dfs(v, t);
flow += f;
e[i].c -= f;
e[i^1].c += f;
}
}
if(!flow) d[x] = -2;
return flow;
}
int dinic(int s, int t)//不断进行构造层次图,然后寻找最短增广路,找出最大流
{
int flow = 0, f;
while(bfs())
{
while(f = dfs(s, INF))
flow += f;
}
return flow;
}
原理:在残量网络上总是沿着最短路进行增广(将费用当做距离来进行考虑)
如果要考虑流量f固定之下的最小费用,可以加一条容量为f从源点出发的边
#define MOD 0x3f3f3f3f
#define V 1000 + 10 //边数
#define E 10000 + 10 //点数
int n, m, flow_sum;// 点数 边数 总流量
bool vis[V];
int cnt, dist[V], head[V], pre[V];
struct Edge
{
int u, v, c, cost, next;//This 'c' means capacity
} edge[E<<2];
void init()
{
cnt = flow_sum = 0;
memset(head, -1, (n+3) * sizeof(int));
}
void adde(int u, int v, int c, int cost)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].c = c;
edge[cnt].cost = cost;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].u = v;
edge[cnt].v = u;
edge[cnt].c = 0;
edge[cnt].cost = -cost;
edge[cnt].next = head[v];
head[v] = cnt++;
}
bool spfa(int begin, int end)
{
int u, v;
queue<int> q;
memset(pre, -1, (n+3)*sizeof(int));
memset(vis, 0, (n+3)*sizeof(bool));
memset(dist, 0x3f, (n+3)*sizeof(int));
vis[begin] = 1;
dist[begin] = 0;
q.push(begin);
while(!q.empty())
{
u = q.front();
q.pop();
vis[u] = false;
for(int i=head[u]; i!=-1; i=edge[i].next)
if(edge[i].c > 0)
{
v = edge[i].v;
if(dist[v] > dist[u] + edge[i].cost)
{
dist[v] = dist[u] + edge[i].cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
return dist[end] != MOD;
}
int MCMF(int begin, int end)
{
int ans = 0, flow;
while(spfa(begin, end))
{
flow = MOD;
for(int i=pre[end]; i!=-1; i=pre[edge[i].u])
flow = min(flow, edge[i].c);
for(int i=pre[end]; i!=-1; i=pre[edge[i].u])
{
edge[i].c -= flow;
edge[i^1].c += flow;
}
ans += dist[end] * flow;
flow_sum += flow;
}
return ans;//返回最小费用
}