HDU - 6582 ( 最短路 + 网络流 )
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6582
题意:
给定一张有向图,n个点,m条边;
可以砍掉一些边,砍掉每条边的代价是边的权值;
问最少花费多少,能使得从1到n的最短路长度至少增加1,不连通也算长度增加。
输出最少花费。
分析:
1.总体思路
由于从1到n的最短路可能存在多条,所以需要先把所有最短路上的边拎出来建一张图G;
然后在图G上跑最小割,用最小的花费把源点1和汇点n割开,割开之后原图中所有原有最短路必然不联通,此时1~n的最短路长度必然增加;
因为“最小割=最大流”定理,所以只需要在图G上跑最大流算法即可。
2.建立图G
如何建立图G呢?
这相当于要找出从1到n的所有最短路。
如果在dijkstra中用vector记录每个节点的多个前驱节点(或边),有可能会MLE;
怎么办呢?
观察每一条边,如果其存在于从1到n的最短路上,那么满足式子:
dist[u]+cost[i]+dist2[v]==dist[n],
其中dist[j]是从1到j的最短路,cost[i]是第i条边的权值,dist2[j]是从j到n的最短路;
dist2是通过反向建图跑dijkstra得到的。
为了防止TLE,这里使用Heap+Dijkstra。
3.最大流
Dinic由于采用分层图、多路增广等策略,时间复杂度比其他网络流算法较优,其时间复杂度是O(n^2*m);
算起来仍然TLE,但网络流算法时间复杂度很奇怪,Dinic跑的很快,加上当前弧优化之后交就是了!
代码:
#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 2e4+10;
int u[maxn],v[maxn],w[maxn];
int dep[maxn],cur[maxn];
int head[maxn],cnt,n,m,s,t;
int dis[2][maxn],via[maxn];
struct node {
int to,nxt,w;
}e[maxn];
typedef struct nod {
int v,date;
} ty;
ty tt,d;
bool operator<( const ty&a, const ty&b )
{
return a.date>b.date;
}
void addage( int u, int v, int w )
{
e[cnt].to = v;
e[cnt].w = w;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
void dijstra( int u, int op )
{
memset(via,0,sizeof(via));
priority_queue<ty> Q;
tt.v = u; tt.date = 0;
Q.push(tt);
while ( !Q.empty() ) {
if ( via[ Q.top().v ]==1 ) {
Q.pop();
continue ;
}
tt = Q.top(); Q.pop();
int u = tt.v;
dis[op][u] = tt.date;
via[u] = 1;
for ( int i=head[u]; i!=-1; i=e[i].nxt ) {
int v = e[i].to,w=e[i].w;
if ( via[v]==0 && dis[op][v]>dis[op][u]+w ) {
dis[op][v] = dis[op][u]+w;
d.v = v; d.date = dis[op][v];
Q.push(d);
}
}
}
}
int bfs( int node )
{
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof(cur));
dep[node] = 1;
queue<int> Q;
Q.push(node);
while ( !Q.empty() ) {
int x = Q.front();Q.pop();
for ( int i=head[x]; i!=-1; i=e[i].nxt ) {
int y = e[i].to,w=e[i].w;
if ( !dep[y] && w ) {
dep[y] = dep[x] + 1;
Q.push(y);
}
}
}
return dep[t];
}
int dfs( int x, int flow )
{
if ( x==t ) return flow;
int sum = 0;
for ( int i=cur[x]; i!=-1; i=e[i].nxt ) {
cur[x] = i;
int y = e[i].to, w=e[i].w;
if ( w && dep[y]==dep[x]+1 ) {
int t = dfs(y,min(flow-sum,w));
sum += t;
e[i].w -= t;
e[i^1].w += t;
if ( sum==flow ) break;
}
}
if ( sum==0 ) dep[x] = 0;
return sum;
}
signed main()
{
int T;cin>>T;
while ( T-- ) {
cin >> n >> m;
memset(head,-1,sizeof(head)); cnt = 0;
memset(dis,inf,sizeof(dis));
for ( int i=0; i<m; i++ ) {
scanf("%lld %lld %lld",&u[i],&v[i],&w[i]);
addage(u[i],v[i],w[i]);
}
dijstra(1,0);
memset(head,-1,sizeof(head)); cnt = 0;
for ( int i=0; i<m; i++ ) {
addage(v[i],u[i],w[i]);
}
dijstra(n,1);
memset(head,-1,sizeof(head)); cnt = 0;
for ( int i=0; i<m; i++ ) {
if ( dis[0][ u[i] ] + w[i] + dis[1][ v[i] ]==dis[0][n] ) {
addage(u[i],v[i],w[i]);
addage(v[i],u[i],0);
}
}
int ans = 0;
s = 1; t = n;
while ( bfs(s) ) {
ans += dfs(s,inf);
}
cout << ans << endl;
}
return 0;
}