[20220210联考] 一般图带权多重匹配

前言

成功向着成功相反的方向一直走可能也算一种成功吧qwq。

题目

链接就算了。

\(n\) 个自然数 \(a_i\),以及 \(c_{i,j}\) 表示将 \(a_i\)\(a_j\) 都减一的代价,注意 \(i,j\) 可以相等,此时相当于一个数减两次。

询问把所有数减到 \(0\) 的最小花费。无解输出 \(-1\)。数据范围中的 \(k\) 表示 \(a_i\) 为奇数的个数。

\(1\le n\le 50;k\le 8;c_{i,j}=c_{j,i}\le 100000;a_{i}\le 100.\)

讲解

这么小的数据,这么小的代价,这长得也太网络流了吧!

由于我见题不多,所以我就开始思考怎么让一个代价有两个固定点的流量。

我想的是新建虚点,到虚点的边控制花费,然后虚点连出去的边控制流量,但是我并不会均分出去的流量,遂自闭。

然而正解完全相反,两个点之间的边来控制花费,而点与源汇点的边控制流量!

具体来说,我们将一次 \(c_{i,j}\) 的操作看作从 \(i\) 连向 \(j\) 的一条边,然后将 \(a_i\) 均分为入度和出度,至于奇数点只需暴力枚举入度多 \(1\) 还是出度多 \(1\),也就 \(C_{k}^{k/2}\) 至多 \(C_{8}^{4}\) 种情况。整个过程还可以看成欧拉路。

正确性显然。

时间复杂度不会算,总之就是情况数量乘上费用流的时间。

代码

//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int n,k,S,T,ans = INF;
int a[MAXN],c[MAXN][MAXN];

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int head[MAXN],tot = 1;
struct edge
{
	int v,w,c,nxt;
}e[MAXN*MAXN<<2];
void Add_Edge(int u,int v,int w,int c)
{
	e[++tot] = edge{v,w,c,head[u]};
	head[u] = tot;
}
void Add_Double_Edge(int u,int v,int w,int c)
{
	Add_Edge(u,v,w,c);
	Add_Edge(v,u,0,-c);
}
bool tag[MAXN],inq[MAXN];
vector<int> odd;
int pre[MAXN][2],flow[MAXN],dis[MAXN];
bool bfs()
{
	for(int i = 1;i <= T;++ i) dis[i] = INF,flow[i] = 0;
	dis[S] = 0; flow[S] = INF;
	queue<int> q; q.push(S);
	while(!q.empty())
	{
		int x = q.front(); q.pop(); inq[x] = 0;
		for(int i = head[x],v; i ;i = e[i].nxt)
			if(e[i].w > 0 && dis[x] + e[i].c < dis[v = e[i].v])
			{
				dis[v] = dis[x] + e[i].c;
				flow[v] = Min(flow[x],e[i].w);
				pre[v][0] = x;
				pre[v][1] = i;
				if(!inq[v]) inq[v] = 1,q.push(v);
			}
	}
	return dis[T] < INF;
}
int MCMF()
{
	int ret = 0;
	while(bfs())
	{
		ret += flow[T] * dis[T];
		for(int now = T;now ^ S;now = pre[now][0])
		{
			e[pre[now][1]].w -= flow[T];
			e[pre[now][1]^1].w += flow[T];
		}
	}
	return ret;
}
void solve()
{
	tot = 1;
	for(int i = 1;i <= T;++ i) head[i] = 0;
	for(int i = 1;i <= n;++ i) 
		if(a[i] & 1)
		{
			if(tag[i]) Add_Double_Edge(S,i,(a[i]>>1)+1,0),Add_Double_Edge(i+n,T,a[i]>>1,0);
			else Add_Double_Edge(S,i,a[i]>>1,0),Add_Double_Edge(i+n,T,(a[i]>>1)+1,0);
		}
		else Add_Double_Edge(S,i,a[i]>>1,0),Add_Double_Edge(i+n,T,a[i]>>1,0);
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j <= n;++ j)
			Add_Double_Edge(i,j+n,INF,c[i][j]);
	ans = Min(ans,MCMF());
}
void dfs(int x,int les)
{
	if(x == k)
	{
		solve();
		return;
	}
	if(k-x-1 >= les) tag[odd[x]] = 0,dfs(x+1,les);
	if(les) tag[odd[x]] = 1,dfs(x+1,les-1);
}

int main()
{
//	freopen("match.in","r",stdin);
//	freopen("match.out","w",stdout);
	n = Read();
	for(int i = 1;i <= n;++ i) 
		if((a[i] = Read()) & 1)
			odd.emplace_back(i);
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j <= n;++ j)
			c[i][j] = Read();
	if((k = odd.size()) & 1) {Put(-1,'\n');return 0;}
	S = n<<1|1; T = S+1; dfs(0,k>>1);
	Put(ans,'\n');
	return 0;
}
posted @ 2022-02-10 21:37  皮皮刘  阅读(71)  评论(0编辑  收藏  举报