【BUAA Spring Training 07】最大流 | 最小费用流 | 可行流 | 二分图 | H

第七场春训,谢谢szh、zkj学长 qwq!
补题式学习法


【BUAA Spring Training 07】


Tags:最大流 费用流 可行流 二分图




Problem A. Petya and Graph


[A] 题意

一个子图是由原图边的子集和点的子集构成的图(每条边的端点必须是点子集中的点)

子图的权值总和定义为这个子图的边权和减去点权和。

给定一个没有自环重边的无向图,求最大子图权值。点数边数都 ∈ [ 1 , 1 0 3 ] \in [1, 10^3] [1,103]


[A] 思路

看到这个范围就应该想到是网络流…

我们把每条边视为一个中介点,那么问题就转换为了典型的最大权闭合子图

建图方式就是,原图每个结点以点权为容量连向超级汇,然后把每条边拆成一个中介点,从超级源拉一条容量为边权的弧过来,接着再把中介点和原边的两个端点以无穷容量连接就行了。

然后把原图边权全部相加,再减去建图后的最大流就是答案。


时间复杂度:网络流,你懂的, O ( O( O(能过 ) ) )



[A] 代码

#include <cstdio>
#include <cstring>
#include <limits>

#define LL long long
#define ULL unsigned LL

#define _BS 1048576
char _bf[_BS];
char *__p1=_bf,*__p2=_bf;
#define _IO char *_p1=__p1,*_p2=__p2;
#define _OI __p1=_p1,__p2=_p2;

#ifdef _KEVIN
#define GC getchar()
#else
#define GC (_p1==_p2&&(_p2=(_p1=_bf)+fread(_bf,1,_BS,stdin),_p1==_p2)?EOF:*_p1++)
#endif

#define PC putchar
#define _Q_(x) {register char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;
#define _H_(x) for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define sc(x) _Q_(x)_H_(x)
#define se(x) _Q_(x)else if(_c==EOF)return 0;_H_(x)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define _F0N(i,n) for(auto i=0;i<(n);++i)
#define _FLR(i,l,r) for(auto i=(l);i<(r);++i)
#define _gF(_1, _2, _3, _F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(auto i=0;i<=(n);++i)
#define _FDL(i,l,r) for(auto i=(l);i<=(r);++i)
#define _gFD(_1, _2, _3, _FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)
#define FRC(R,C) for(int r=0;r<R;++r)for(int c=0;c<C;++c)


template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}
#define UPL(_) UPRT(_),PC(10)
template<class T>
void PRT(const T _){if(_<0){PC(45),UPRT<ULL>(-(ULL)_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
#define PL(_) PRT(_),PC(10)

#define MAX(a, b) ((a)>(b)?(a):(b))
#define MIN(a, b) ((a)<(b)?(a):(b))


constexpr int MV(6e3+7), ME(MV<<1);


template <typename cint>
class Dinic
{
private:

	static constexpr cint CINT_MAX = std::numeric_limits<cint>::max();
	struct Ed
	{
		int ne, u, v;
		cint c;
	} ed[ME];
	int head[MV], cur[MV], tot;
	int V, S, T;
	
	int de[MV];
	bool bfs()
	{
		static int q[MV];
		int hd = 0, tl = 0;

		memset(de, 0, sizeof(*de) * (V+2));
		de[q[tl++]=S] = 1;
		while (hd != tl)
		{
			const int u = q[hd++];
			for (int i=head[u]; i; i=ed[i].ne)
			{
				const int v = ed[i].v;
				if (ed[i].c && !de[v])
				{
					de[q[tl++]=v] = de[u] + 1;
					if (v == T)
						return true;
				}
			}
		}
		
		return false;
	}
	
	cint dfs(const int u, cint in)
	{
		if (u == T)
			return in;
		cint out = 0;
		for (int i=cur[u]; in&&(cur[u]=i); i=ed[i].ne)
		{
			if (ed[i].c>0 && de[ed[i].v]==de[u]+1)
			{
				const cint tp = dfs(ed[i].v, MIN(in, ed[i].c));
				in -= tp, out += tp, ed[i].c -= tp, ed[i^1].c += tp;
			}
		}
		
		if (in || !out)
			de[u] = -1;
		return out;
	}
	
public:
	
	void init(const int v)
	{
		if (V)
			memset(head, 0, sizeof(*head) * (V+2));
		V = v;
		tot = 1;
	}
	
	void edd(const int u, const int v, const cint c = CINT_MAX)
	{
		ed[++tot].ne=head[u], ed[tot].u=u, ed[tot].v=v, ed[tot].c=c, head[u]=tot;
		ed[++tot].ne=head[v], ed[tot].u=v, ed[tot].v=u, ed[tot].c=0, head[v]=tot;
	}
	
	cint max_flow(const int s, const int t)
	{
		cint sum = 0;
		S = s, T = t;
		while (bfs())
		{
			memcpy(cur, head, sizeof(*head) * (V+2));
			sum += dfs(s, CINT_MAX);
		}
		return sum;
	}
};

Dinic<LL> mf;

int main()
{
	_IO
	get(V, E);
	const int S = 0, T = V+E+1;
	mf.init(T-S+1);
	
	FD(i, 1, V)
	{
		get(ai)	// cost
		mf.edd(i, T, ai);
	}
	
	LL sum = 0;
	FD(i, 1, E)
	{
		get(u, v, c)
		sum += c;
		mf.edd(V+i, u);
		mf.edd(V+i, v);
		mf.edd(S, V+i, c);
	}
	
	PL(sum - mf.max_flow(S, T));
	
#ifdef _KEVIN
	getchar();
#endif
	return 0;
}




Problem B. Scaygerboss

(待更)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值