杂题

题意

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

做法

写成线性规划的形式:

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

(z=frac{S}{C})

[egin{aligned} & ext{minimize} sum x_i+sum y_i+Ccdot z\ & ext{s.t.}egin{cases} x_i+y_j+zge a_{i,j}&(1le i,jle n)\ x_ige 0,y_ige 0,zge 0 end{cases} end{aligned}]

写成对偶的形式:

[egin{aligned} & ext{maximize} sumlimits_{i=1}^n sumlimits_{j=1}^n a_{i,j}x_{i,j}\ & ext{s.t.}egin{cases} sumlimits_{j=1}^n x_{i,j}le 1&(1le ile n)\ sumlimits_{i=1}^n x_{i,j}le 1&(1le jle n)\ sumlimits_{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
",ans[std::min(C,n)]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Grice/p/14509486.html