HDU6582 Path【优先队列优化最短路 + dinic最大流 == 最小割】

时间:2023-06-13 20:33:56

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6582

来源:2019 Multi-University Training Contest 1

题目大意:

给定一张有向图,可以阻碍若干条有向边,花费为边的权值,求使其最短路变得更长所需的最小花费。

解题思路:

1.因为最短路可能是多条,所以找出最短路网络,然后在最短路网络中跑最小割,即最大流。就切断了原先的最短路且保证了是最小花费(最小割)。

2.值得注意的地方:边的长度限制为1e9,所以最短路数组以及一些其他的求和的值要开long long型,防止爆。

3.求最短路核心边需满足 dis[u] + edge[i].w == dis[v]。那么edge[i].w就是一条最短路的核心边。同时要确保合法性,满足dis[u] != inf && dis[v] != inf

代码如下:

#include<stdio.h>
#include<string.h>
#include<queue>
#define LL long long
#define mem(a, b) memset(a, b, sizeof(a))
const int MAXN = 1e4 + ;
const LL inf = 0x3f3f3f3f;
using namespace std; int n, m;//n个点 m条有向边
int vis[MAXN];//源点到该点的最短距离是否被确定
LL dis[MAXN]; struct Edge
{
int to, next;
LL w;
}e[MAXN];
int head[MAXN], cnt; void add(int a, int b, int c)
{
e[++ cnt].to = b;
e[cnt].w = c;
e[cnt].next = head[a];
head[a] = cnt;
} struct Node
{
int pot;
LL dis; //点 以及 源点到该点的最短距离
bool operator < (const Node &a)const//重载 按照dis从小到大排序
{
return dis > a.dis;
}
}no; priority_queue<Node> Q;
void dij() //优先队列优化的最短路
{
mem(vis, ), mem(dis, inf);
no.dis = , no.pot = ;
dis[] = ;
Q.push(no);
while(!Q.empty())
{
Node a = Q.top();//优先队列无front操作
Q.pop();
if(vis[a.pot])
continue;
vis[a.pot] = ;
for(int i = head[a.pot]; i != -; i = e[i].next)//松弛操作
{
int to = e[i].to;
if(!vis[to] && dis[a.pot] + e[i].w < dis[to])
{
dis[to] = dis[a.pot] + e[i].w;
no.pot = to, no.dis = dis[to];
Q.push(no);
}
}
}
// printf("%d\n", dis[n]);
} struct edge_
{
int to, next, flow;
}e_[ * MAXN];//要加反向边 开2倍
int head_[MAXN]; void add_(int a, int b, int c)
{
e_[++ cnt].to = b;
e_[cnt].flow = c;
e_[cnt].next = head_[a];
head_[a] = cnt;
} int dep[MAXN];
int bfs(int st, int ed)
{
if(st == ed)
return ;
mem(dep, -);
queue<int >Q_;
dep[st] = ;
Q_.push(st);
while(!Q_.empty())
{
int now = Q_.front();
Q_.pop();
for(int i = head_[now]; i != -; i = e_[i].next)
{
int to = e_[i].to;
if(e_[i].flow > && dep[to] == -)
{
dep[to] = dep[now] + ;
Q_.push(to);
}
}
}
return dep[ed] != -;
} int dfs(int now, int ed, int inc)
{
if(now == ed)
return inc;
for(int i = head_[now]; i != -; i = e_[i].next)
{
int to = e_[i].to;
if(e_[i].flow > && dep[to] == dep[now] + )
{
int min_flow = dfs(to, ed, min(inc, e_[i].flow));
if(min_flow > )
{
e_[i].flow -= min_flow;
e_[i ^ ].flow += min_flow;
return min_flow;
}
}
}
return -;
} LL dinic(int st, int ed)
{
LL ans = ;
while(bfs(st, ed))
{
while()
{
int inc = dfs(st, ed, inf);
if(inc == -)
break;
ans += inc;
}
}
return ans;
} int main()
{
int T;
scanf("%d", &T);
while(T --)
{
scanf("%d%d", &n, &m);
cnt = , mem(head, -); //最短路的边从1开始存 head初始化为-1
for(int i = ; i <= m; i ++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c); //有向边 加一条即可
}
dij(); //跑最短路
if(dis[n] == inf) //特判
{
printf("0\n");
continue;
}
cnt = -, mem(head_, -);//从0开始存 才能进行 ^ 运算
for(int i = ; i <= n; i ++)
{
for(int j = head[i]; j != -; j = e[j].next)
{
int to = e[j].to;
if(dis[i] + e[j].w == dis[to] && dis[to] != inf && dis[i] != inf) //得到最短路核心边(因为可能是多条)
{
add_(i, to, e[j].w);
add_(to, i, ); //反向边容量为 0
}
}
}
printf("%lld\n", dinic(, n));
}
return ;
}

优先队列优化dij+最大流dinic