杂题
题意
给定一个带边权的\(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;
}