[jzoj5972]wang(最小权匹配=最小费用最大流、贪心、结论题)

版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/84913470
5972. 【北大2019冬令营模拟12.1】 wang(2s,256MB)
Problem
  • 给定一个定义域和值域都在 Z Z 上的函数 F ( x ) F(x) ,且给定一个常数 C C ,且满足 F ( 2 F ( x ) x + 1 ) = F ( x ) + C F(2F(x) - x + 1) = F(x) + C

  • 现在给定你 n n 个二元组 ( x i , y i ) (x_i,y_i) ,要求最小化 i = 1 n F ( x i ) y i \sum_{i=1}^n|F(x_i)-y_i|

Data constraint
  • C 60 C\le 60

  • n 10000 n\le 10000

Solution
  • 性质①

    F ( x + 2 C ) = F ( x ) + 2 C F(x+2C)=F(x)+2C

  • 证明①

  • y = 2 F ( x ) x + 1 y=2F(x)-x+1


  • F ( y ) = F ( x ) + C F(y)=F(x)+C \Downarrow F ( y ) + C = F ( x ) + 2 C F(y)+C=F(x)+2C \Downarrow F ( 2 F ( y ) y + 1 ) = F ( x ) + 2 C F(2F(y)-y+1)=F(x)+2C 2 F ( y ) y + 1 = 2 F ( y ) 2 F ( x ) + x = x + 2 C 2F(y)-y+1=2F(y)-2F(x)+x=x+2C \Downarrow F ( x + 2 C ) = F ( x ) + 2 C F(x+2C)=F(x)+2C

  • 性质②

    • a + b = 2 t + 1 , F ( a ) = t a+b=2t+1,F(a)=t

    • F ( a ) + F ( b ) = 2 t + C F(a)+F(b)=2t+C

证明②

  • a = 2 a , b = 2 b + 1 , t = a + b a=2a',b=2b'+1,t=a'+b'
  • F ( 2 F ( a ) a + 1 ) = F ( a ) + C F(2F(a)-a+1)=F(a)+C \Downarrow F ( 2 ( t + n C ) a + 1 ) = t + n C + C F(2(t+nC)-a+1)=t+nC+C \Downarrow F ( 2 t a + 1 + 2 n C ) = t + n C + C F(2t-a+1+2nC)=t+nC+C \Downarrow F ( 2 t a + 1 ) + 2 n C = t + n C + C F(2t-a+1)+2nC=t+nC+C \Downarrow F ( b ) = t ( n 1 ) C F(b)=t-(n-1)C
  • 然后根据这两个性质,把所有的 X i X_i 按照 2 C 2C 取模之后可以分成 2 C 2C 组,每一组当中只要一个对应的值确立,整个组的值便可以确立.

  • 所以我们只需考虑组与组之间的对应关系

  • 并且根据题意,当 F ( a ) F(a) 等于某个值 t t 时,只有 F ( 2 t a + 1 ) F(2t-a+1) F ( a ) F(a) 有关系,所以,很明显只有奇偶性不同的组互相影响.

  • 根据性质②,我们得知当 F ( a ) = t + n C F(a)=t+nC 时,对应的 F ( 2 t a + 1 ) = t ( n 1 ) C F(2t-a+1)=t-(n-1)C ,换言之,对于所有的 X i X_i ,若其属于某组 a a (即 X i a ( m o d   2 C ) X_i\equiv a(mod\ 2C) ),那么可以考虑与其奇偶性不同的所有组 b b ,组 a a 与不同的组 b b 进行影响会产生不同的代价,而我们需要做的,是让所有的奇数组偶数组进行完美配对.

  • 要明白这一点,首先要知道什么叫完美配对.

  • 给定一个二分图,如果可以分成两个相等集合 S , T S,T ,并且 S S 中每个点与且仅与 T T 中一个点进行匹配,那么这个二分图就可以完美配对.

  • 对于此题而言,因为每个 F ( a ) F(a) 影响且仅能影响一个对应的 F ( b ) F(b) ,并且影响了 F ( b ) F(b) 之后,既不能影响另一个 F ( c ) F(c) ,又不能被另一个 F ( c ) F(c) 影响,所以这显然是一个二分图,显然是可以进行完美配对的。

  • 为了更好地计算出两组 a , b a,b 相互影响的代价,观察到实际上 F ( a ) F(a) 只能变成 t + n C t+nC 的形式,而 F ( b ) F(b) 也是类似于这个形式,所以我们要计算 i = 1 n F ( X i ) Y i \sum_{i=1}^n|F(X_i)-Y_i| 的代价,就可以想办法把式子转化成类似于 i = 1 n W i + n C \sum_{i=1}^n|W_i+n*C| 的形式,事实证明,这很容易做到.

  • 考虑两组 a , b a,b ,不妨假设 a = 2 a , b = 2 b + 1 a=2a',b=2b'+1 ,那么如果一个 X i X_i 在对应的 a a 组,我们就令 W i = F ( X i ) Y i = F ( a ) + X i a Y i = a + b + X i a Y i W_i=F(X_i)-Y_i=F(a)+X_i-a-Y_i=a'+b'+X_i-a-Y_i

  • W i = a + b + X i a Y i W_i=a'+b'+X_i-a-Y_i .

  • 反之,如果 X i X_i b b 组,我们就令 W i = F ( X i ) Y i = F ( b ) + X i b Y i = a + b + C + X i b Y i W_i=F(X_i)-Y_i=F(b)+X_i-b-Y_i=a'+b'+C+X_i-b-Y_i ,因为 W i W_i 在式子里是套上了绝对值的,所以我们可以把它变成 W i = Y i + b a b X i C W_i=Y_i+b-a'-b'-X_i-C

  • W i = Y i + b a b X i C W_i=Y_i+b-a'-b'-X_i-C

  • 这样一来,所有的 F ( X i ) Y i |F(X_i)-Y_i| 都可以变化成 i = 1 n W i + n C \sum_{i=1}^n|W_i+n*C| .

  • 我们取中位数,并适当调整,可以很容易确定一个最优的 n n .

  • 这样子便可以计算出两组 a , b a,b 进行配对的最优代价,接下来就是如何最小权匹配了.

  • 对于这个算法,我们可以直接转化最小费用最大流.

  • 最小费用最大流需要注意它是在保证最大流的前提下最小费用的,所以我们可以按照正常思维,从原点向左边一排点连容量为 1 1 ,费用为 0 0 的边,汇点同样向右边一排点这样连边,之后,对于中间的连边,流量为 1 1 ,费用就是匹配代价,之后用 Z K W ZKW 算法跑一遍就可以得到答案了.

  • 因为本人太菜,所以这里大致讲一讲费用流的实现.

  • 求解费用流一般有两种算法:第一种就是刚刚提到的大名鼎鼎的 Z K W ZKW 算法,也是实战中经常用到的算法,另外一种就是经典的 s p f a spfa 增广算法.

SPFA增广
  • 一句话,每次找一条费用最小的流进行增广,直到不能增广为止.
#define I register int

void link(I x, I y, I z, I l) {
	tov[++ tot] = y, len[tot] = z, nex[tot] = las[x], cost[tot] =  l, las[x] = tot;
	tov[++ tot] = x, len[tot] = 0, nex[tot] = las[y], cost[tot] = -l, las[y] = tot;
}

bool spfa() {
	queue<int> q;
	mem(d, 0xcf), mem(v, 0), q.push(S), d[S] = 0, v[S] = 1, incf[S] = 1 << 30;
	while (q.size()) {
		I x = q.front(); v[x] = 0; q.pop();
		for (I i = las[x], y = tov[i]; i; i = nex[i], y = tov[i])
			if (len[i] && d[y] < d[x] + cost[i]) {
				d[y] = d[x] + cost[i], incf[y] = min(incf[x], len[i]), pre[y] = i;
				if (!v[y]) v[y] = 1, q.push(y);
			}
	}
	return d[T] > 0;
}

void update() {
	for (I x = T; x ^ S;)
		len[pre[x]] -= incf[T], len[pre[x] ^ 1] += incf[T], x = tov[pre[x] ^ 1];
	maxflow += incf[T], ans += d[T] * incf[T];
}

while (spfa()) update();
ZKW算法
#define ll long long

void link(ll x, ll y, ll l, ll L) {
	tv[++ tt] = y, c[tt] = L, nx[tt] = ls[x], ls[x] = tt, len[tt] =  l;
	tv[++ tt] = x, c[tt] = 0, nx[tt] = ls[y], ls[y] = tt, len[tt] = -l;
} //c[x]表示x这条边的流量,len[x]表示x这条边的费用


bool bfs() { //这里与spfa增广几乎是一模一样的,核心都是找出从原点到所有点的最小费用.
	mem(vis, 0), mem(dis, 0x7f), dis[S] = 0, vis[S] = 1;
	queue <ll> Q; Q.push(S);
	
	while (!Q.empty()) {
		ll k = Q.front(); Q.pop();
		for (ll x = ls[k]; x ; x = nx[x])
			if (c[x] && dis[tv[x]] > dis[k] + len[x]) {
				dis[tv[x]] = dis[k] + len[x]; //更新最小费用
				if (!vis[tv[x]])
					vis[tv[x]] = 1, Q.push(tv[x]);
			}
		vis[k] = 0;
	}

	return dis[T] < 9187201950435737471;
}

ll dfs(ll k, ll flow) {
	if (k == T) { vis[T] = 1; return flow; }
	ll have = 0; vis[k] = 1;
	//这里一定要注意这个vis标记的作用,因为dis[i]表示的是最小费用,而并非普通dinic的距离标号
	//所以这是可能存在零环的,因此一定要用个标记来去掉零环带来的影响.
	for (ll x = ls[k]; x ; x = nx[x])
		if (!vis[tv[x]] && c[x] && dis[tv[x]] == dis[k] + len[x]) {
			ll now = dfs(tv[x], min(flow - have, c[x]));
			ans += now * len[x], c[x] -= now, c[x ^ 1] += now, have += now;
			if (flow == have) return have;
		}
	return have;
}

int main() {
	while (bfs())
		for (vis[T] = 1; vis[T]; )
			mem(vis, 0), dfs(S, inf);
}

  • 实际上 Z K W ZKW 的算法就是借鉴了普通的最大流 d i n i c dinic 算法以及原始 s p f a spfa 算法。

  • 在求出了 d i s [ i ] dis[i] ,即最小花费之后每次多路增广,而不仅仅只是增广一条最优的路径(这是 s p f a spfa 增广),这样可以少进行很多次 s p f a spfa ,提高效率.

  • 所以综上,也不难发现,在稠密图上 Z K W ZKW 一般是占据优势的,但在极稀疏的图上, s p f a spfa 增广一般更优.

猜你喜欢

转载自blog.csdn.net/Algor_pro_king_John/article/details/84913470