杂题

题意

给定一个带边权的\(K_{n,n}\),边权表示为\(\{a_{i,j}\}\)
\(q\)次询问,每次给定\(C\)
为左部和右部点分别设置一个顶标\(\{x_i\},\{y_i\}\)
你要格外确定一个花费\(S\),使得\(\forall i,j,\text{s.t.} x_i+y_j\ge a_{i,j}-\frac{S}{C}\),最小化\(\sum x_i+\sum y_i+S\)
顶标与花费均为非负实数。
\(N\le 500,a_{i,j}\in[0,10^9],C\in (0,10^9]\)

做法

写成线性规划的形式:

\[\begin{aligned} &\text{minimize} \sum x_i+\sum y_i+S\\ &\text{s.t.}\begin{cases} x_i+y_j+\frac{S}{C}\ge a_{i,j}&(1\le i,j\le n)\\ x_i\ge 0,y_i\ge 0,S\ge 0 \end{cases} \end{aligned}\]

\(z=\frac{S}{C}\)

\[\begin{aligned} &\text{minimize} \sum x_i+\sum y_i+C\cdot z\\ &\text{s.t.}\begin{cases} x_i+y_j+z\ge a_{i,j}&(1\le i,j\le n)\\ x_i\ge 0,y_i\ge 0,z\ge 0 \end{cases} \end{aligned}\]

写成对偶的形式:

\[\begin{aligned} &\text{maximize} \sum\limits_{i=1}^n \sum\limits_{j=1}^n a_{i,j}x_{i,j}\\ &\text{s.t.}\begin{cases} \sum\limits_{j=1}^n x_{i,j}\le 1&(1\le i\le n)\\ \sum\limits_{i=1}^n x_{i,j}\le 1&(1\le j\le n)\\ \sum\limits_{i=1}^n x_{i,j}\le C\\ x_{i,j}\ge 0\\ \end{cases} \end{aligned}\]

可以看作是二分图匹配:将\(x_{i,j}\)值域定义为\(\{0,1\}\),感性理解挺对的。

然后就是限制匹配数为\(C\)的最大权二分图匹配。

可以用KM实现

code

放份板子

#include<bits/stdc++.h>
typedef long long LL;
typedef double dl;
#define opt operator
#define pb push_back
#define pii std::pair<LL,LL>
const LL maxn=509,mod=998244353,inf=1e9;
LL Read(){
	LL x(0),f(1); char c=getchar();
	while(c<'0' || c>'9'){
		if(c=='-') f=-1; c=getchar();
	}
	while(c>='0' && c<='9'){
		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
	}return x*f;
}
void Chkmin(LL &x,LL y){
	if(y<x) x=y;
}
void Chkmax(LL &x,LL y){
	if(y>x) x=y;
}
LL add(LL x,LL y){
	return x+=y,x>=mod?x-mod:x;
}
LL dec(LL x,LL y){
	return x-=y,x<0?x+mod:x;
}
LL mul(LL x,LL y){
	return 1ll*x*y%mod;
}
LL Pow(LL base,LL b){
	LL ret(1); while(b){
		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
	}return ret;
}
LL n,q;
LL pre[maxn],a[maxn][maxn],ans[maxn],mtx[maxn],mty[maxn],vall[maxn],valr[maxn];
void Revese(LL y){
	while(y){
		LL x(pre[y]); LL tmp(mty[x]);
		mty[x]=y; mtx[y]=x;
		y=tmp;
	}
}
void Bfs(){
	static LL markx[maxn],marky[maxn],slack[maxn];
	for(LL i=1;i<=n;++i){
		markx[i]=marky[i]=pre[i]=0;
		slack[i]=inf;
	}
	std::queue<LL> que;
	for(LL i=1;i<=n;++i) if(!mty[i]){
		que.push(i);
	}
	while(true){
		while(que.size()){
			LL u(que.front()); que.pop();
			markx[u]=1;
			for(LL i=1;i<=n;++i) if(!marky[i]){
				LL gap(vall[u]+valr[i]-a[u][i]);
				if(gap<slack[i]){
					pre[i]=u;
					slack[i]=gap;
					if(!gap){
						if(!mtx[i]){
							Revese(i); return;
						}else{
							marky[i]=1;
							que.push(mtx[i]);
						}
					}
				}
			}
		}
		LL d(inf),pos(0);
		for(LL i=1;i<=n;++i) if(!marky[i]){
			if(slack[i]<d){
				pos=i; d=slack[i];
			}
		}
		for(LL i=1;i<=n;++i){
			if(markx[i]) vall[i]-=d;
			if(marky[i]) valr[i]+=d;
			else slack[i]-=d;
		}
		if(!mtx[pos]){
			Revese(pos); return;
		}
		marky[pos]=1;
		que.push(mtx[pos]);
	}
}
void Fir(){
	for(LL i=1;i<=n;++i){
		vall[i]=1e9;
	}
	for(LL i=1;i<=n;++i){
		valr[i]=0;
	}
	for(LL i=1;i<=n;++i){
		Bfs();
		for(LL j=1;j<=n;++j){
			if(mty[j]){
				ans[i]+=a[j][mty[j]];
			}
		}
	}
}
int main(){
	LL tmp(Read());
	n=Read(); q=Read();
	for(LL i=1;i<=n;++i){
		for(LL j=1;j<=n;++j){
			a[i][j]=Read();
		}
	}
	Fir();
	while(q--){
		LL C(Read());
		printf("%lld.0\n",ans[std::min(C,n)]);
	}
	return 0;
}
posted @ 2021-03-10 09:06  Grice  阅读(65)  评论(0编辑  收藏  举报