网络流 - hdu5294 Tricks Device

探讨了在图论中如何解决最短路径问题及基于此的最小割问题,使用SPFA算法找到最短路径,并通过Dinic算法计算最小割。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5294


题意:

给一个图,2000点,60000边,图中存在多条权值相同的1->n的最短路,第一个问题:删去多少条边后,1->n的最短路径变长;第二个问题:求1->n最短路中,经过节点最少的最短路径的节点数


思路:

在spfa算法中加入一个siz数组,siz[v]记录起点到v点的最短路径经过了多少个节点,即可求出第二个问题的解;

求第二问时得到的dis数组存储了每个点到起点的距离,用这个数组构建新图,把所有最短路上的点和边加入到新图中,所有边的容量记为1,将起点视为源点,终点视为汇点,问题转变成求新图的最小割,即源点到汇点的最大流,最大流算法选择EK会超时,改用dinic后可过


代码:

#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<functional>
#pragma comment(linker, "/STACK:102400000,102400000")//C++
using namespace std;

const double PI = 3.141592653589793238462643383279502884197169399;
const int MAXINT = 0x7fffffff;
const int MAXSIZE = 2000 + 5;
const long long INF = 0x7FFFFFFFFFFFFFFF;

int n,m;
//vector<int> e[MAXSIZE], len[MAXSIZE];
int first[MAXSIZE], gnext[120005], to[120005], wei[120005], edge;
int d[MAXSIZE];
int siz[MAXSIZE];

void init(){
    memset(siz,0,sizeof(siz));
    memset(d,0x1f,sizeof(d));
    memset(first, 0, sizeof(first));
    edge = 0;
}

void g_insert(int a, int b, int c){
    gnext[++edge] = first[a];
    first[a] = edge;
    to[edge] = b;
    wei[edge] = c;
}

void spfa(int s,int t){
	queue<int> Q;
	while (!Q.empty()) Q.pop();
	bool visit[MAXSIZE] = { 0 };
	d[s] = 0;
	visit[s] = true;
	siz[s] = 1;
	Q.push(s);
	while (!Q.empty()){
		int v = Q.front();
		Q.pop();
		visit[v] = false;

		int j=first[v];
		while (j){
            int u = to[j];
			if (d[u] > d[v] + wei[j]){
				d[u] = d[v] + wei[j];
				siz[u] = siz[v] + 1;
				if (!visit[u]){
					Q.push(u);
					visit[u] = true;
				}
			}
			else if (d[u] == d[v] + wei[j]){
                siz[u] = min(siz[u], siz[v]+1);
			}
			j = gnext[j];
		}
	}
	return;
}

struct Edge{
	int from, to, cap;
};

vector<Edge> edges;             //边表,edges[e]和edges[e^1]互为反向边
vector<int> G[MAXSIZE];        //邻接表
int s, t;                  //结点数,边数,源汇点
bool vis[MAXSIZE];
int di[MAXSIZE];                 //层次记录
int cur[MAXSIZE];                //该节点正要考虑的弧的下标

void AddEdge(int from, int to, int cap){
	Edge e = { from, to, cap };
	edges.push_back(e);
	G[from].push_back(edges.size() - 1);
}

void createNewG(){
    for (int i=1;i<=n;++i) G[i].clear();
    edges.clear();
    for (int i=1;i<=n;++i){
        int j=first[i];
        while (j){
            int v=to[j];
            if (d[v] == d[i] + wei[j]){
                AddEdge(i, v, 1);
                AddEdge(v, i, 0);
            }
            j = gnext[j];
        }
    }
    s=1;
    t=n;
}

bool BFS(){
	memset(vis, 0, sizeof(vis));
	queue<int> Q;
	Q.push(s);
	di[s] = 0;
	vis[s] = true;
	while (!Q.empty()) {
		int x = Q.front();
		Q.pop();
		for (int i = 0; i < G[x].size(); ++i) {
			Edge& e = edges[G[x][i]];
			if (!vis[e.to] && e.cap) {
				vis[e.to] = true;
				di[e.to] = di[x] + 1;
				Q.push(e.to);
			}
		}
	}
	return vis[t];
}

int DFS(int x, int a){
	if (x == t || a == 0) return a;
	int flow = 0, f;
	for (int& i = cur[x]; i < G[x].size(); ++i){
		Edge& e = edges[G[x][i]];
		if (di[x] + 1 == di[e.to] && (f = DFS(e.to, min(a, e.cap)))>0){
			e.cap -= f;
			edges[G[x][i] ^ 1].cap += f;
			flow += f;
			a -= f;
			if (a == 0) break;
		}
	}
	return flow;
}

int Maxflow() {
	int flow = 0;
	while (BFS()){
		memset(cur, 0, sizeof(cur));
		flow += DFS(s, MAXSIZE);
	}
	return flow;
}

int main(){
    int a,b,c;
    while (scanf("%d %d",&n,&m)!=EOF){
        init();
        for (int i=0;i<m;++i){
            scanf("%d %d %d",&a,&b,&c);
            g_insert(a,b,c);
            g_insert(b,a,c);
        }

        spfa(1,n);
        createNewG();

        int temp;
        temp = Maxflow();
        cout<<temp<<" "<<m-(siz[n]-1)<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值