学姐的逛街计划-最大费用最大流,线性规划
题目描述
题解
设第
i
i
i天是否去逛街为
a
[
i
]
a[i]
a[i],
c
[
i
]
c[i]
c[i]表示第i天的智商,
a
[
i
]
=
1
a[i]=1
a[i]=1表示去逛街,
a
[
i
]
=
0
a[i]=0
a[i]=0表示不去
则可得
2
n
2n
2n个不等式
a
[
1
]
+
a
[
2
]
+
.
.
.
+
a
[
n
]
<
=
k
a[1]+a[2]+...+a[n]<=k
a[1]+a[2]+...+a[n]<=k
a
[
2
]
+
a
[
3
]
+
.
.
.
+
a
[
n
+
1
]
<
=
k
a[2]+a[3]+...+a[n+1]<=k
a[2]+a[3]+...+a[n+1]<=k
.
.
.
...
...
a
[
2
n
+
1
]
+
.
.
.
.
+
a
[
3
n
]
<
=
k
a[2n+1]+....+a[3n]<=k
a[2n+1]+....+a[3n]<=k
求
c
[
1
]
∗
a
[
1
]
+
c
[
2
]
∗
a
[
2
]
+
.
.
.
+
c
[
3
n
]
∗
a
[
3
n
]
c[1]*a[1]+c[2]*a[2]+...+c[3n]*a[3n]
c[1]∗a[1]+c[2]∗a[2]+...+c[3n]∗a[3n]的最大值
添加一个辅助变量
a
[
1
]
+
a
[
2
]
+
.
.
.
+
a
[
n
]
+
y
[
1
]
=
k
a[1]+a[2]+...+a[n]+y[1]=k
a[1]+a[2]+...+a[n]+y[1]=k
a
[
2
]
+
a
[
3
]
+
.
.
.
+
a
[
n
+
1
]
+
y
[
2
]
=
k
a[2]+a[3]+...+a[n+1]+y[2]=k
a[2]+a[3]+...+a[n+1]+y[2]=k
.
.
.
...
...
a
[
2
n
+
1
]
+
.
.
.
.
+
a
[
3
n
]
+
y
[
2
n
+
1
]
=
k
a[2n+1]+....+a[3n]+y[2n+1]=k
a[2n+1]+....+a[3n]+y[2n+1]=k
0
<
=
y
[
i
]
<
=
k
0<=y[i]<=k
0<=y[i]<=k
将上述不等式相邻两个相减
y
[
1
]
+
a
[
1
]
=
a
[
n
+
1
]
+
y
[
2
]
−
−
−
(
1
)
y[1]+a[1]=a[n+1]+y[2]---(1)
y[1]+a[1]=a[n+1]+y[2]−−−(1)
y
[
2
]
+
a
[
2
]
=
a
[
n
+
2
]
+
y
[
3
]
−
−
−
(
2
)
y[2]+a[2]=a[n+2]+y[3]---(2)
y[2]+a[2]=a[n+2]+y[3]−−−(2)
.
.
.
.
.
.
......
......
y
[
n
+
1
]
+
a
[
n
+
1
]
=
a
[
2
n
+
1
]
+
y
[
n
+
2
]
−
−
−
(
n
+
1
)
y[n+1]+a[n+1]=a[2n+1]+y[n+2]---(n+1)
y[n+1]+a[n+1]=a[2n+1]+y[n+2]−−−(n+1)
.
.
.
.
.
.
......
......
y
[
2
n
]
+
a
[
2
n
]
=
a
[
3
n
]
+
y
[
2
n
+
1
]
−
−
−
(
2
n
)
y[2n]+a[2n]=a[3n]+y[2n+1]---(2n)
y[2n]+a[2n]=a[3n]+y[2n+1]−−−(2n)
根据网络中每个节点流入量等于流出量的性质
将上述等式编号并抽象成网络中的点,变量a[i]和y[i]抽象为网络中的有向边(弧)
问题等价于求最大费用最大流
以
a
[
n
+
1
]
a[n+1]
a[n+1]为例 可以看成是节点1部分流出量和节点
n
+
1
n+1
n+1的部分流入量于是可以建边从
n
+
1
n+1
n+1到
1
1
1
故根据这些等式可以建图
设源点为
0
0
0,汇点为
2
n
+
3
2n+3
2n+3
i
i
i到n+i连一条弧,流量上限为
1
1
1(因为变量a的取值为1或0),费用为
c
[
n
+
i
]
c[n+i]
c[n+i],
1
<
=
i
<
=
n
1<=i<=n
1<=i<=n(针对变量
a
a
a)
i
i
i到
i
+
1
i+1
i+1连一条弧,流量上限为
k
k
k(因为变量y满足
0
<
=
y
<
=
k
0<=y<=k
0<=y<=k),费用为
0
0
0,
1
<
=
i
<
=
2
n
−
1
1<=i<=2n-1
1<=i<=2n−1(针对变量
y
y
y)
这时发现题目的
k
k
k还没用上,
于是发现上述等式成立必需满足这两个等式
a
[
1
]
+
a
[
2
]
+
.
.
.
+
a
[
n
]
+
y
[
1
]
=
k
−
−
−
(
n
+
1
)
a[1]+a[2]+...+a[n]+y[1]=k---(n+1)
a[1]+a[2]+...+a[n]+y[1]=k−−−(n+1)
a
[
2
n
+
1
]
+
.
.
.
.
+
a
[
3
n
]
+
y
[
2
n
+
1
]
=
k
−
−
−
(
2
n
+
2
)
a[2n+1]+....+a[3n]+y[2n+1]=k---(2n+2)
a[2n+1]+....+a[3n]+y[2n+1]=k−−−(2n+2)
于是建一个节点
2
n
+
1
2n+1
2n+1
为了满足
2
n
+
1
2n+1
2n+1式
则由源点向节点2n+1连一条流量上限为
k
k
k的边,费用为0。
由节点
2
n
+
1
2n+1
2n+1向i连一条流量上限为
1
1
1的边,费用为
c
[
i
]
c[i]
c[i],
1
<
=
i
<
=
n
1<=i<=n
1<=i<=n
由
源
点
源点
源点向
2
n
+
1
2n+1
2n+1连一条流量上限为
k
k
k的边,费用为
0
0
0
同理建一个节点
2
n
+
2
2n+2
2n+2
为了满足
2
n
+
2
2n+2
2n+2式
则由节点
2
n
+
2
2n+2
2n+2向汇点连一条流量上限为
k
k
k的边,费用为
0
0
0。
由节点i向
2
n
+
2
2n+2
2n+2连一条流量上限为
1
1
1的边,费用为
c
[
i
]
c[i]
c[i]
n
+
1
<
=
i
<
=
2
n
n+1<=i<=2n
n+1<=i<=2n
建图完毕,剩下就是套算法。
代码
#include<bits/stdc++.h>
#define M 200009
using namespace std;
const int inf=1e9+7;
int nxt[M],to[M],f[M],w[M],first[M],tot=1;
int vis[M],d[M],now[M],n,m,ret,a[M],k,T;
void add(int x,int y,int z,int v){
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z,f[tot]=v;
nxt[++tot]=first[y],first[y]=tot,to[tot]=x,w[tot]=0,f[tot]=-v;
}
bool bfs(){
memset(vis,0,sizeof(vis));
for(int i=0;i<=T;i++) d[i]=-1;
queue<int>q;
d[T]=0,vis[T]=1;
q.push(T),now[T]=first[T];
while(q.size()){
int u=q.front();
q.pop(),vis[u]=0;
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
if(w[i^1]&&d[v]<d[u]+f[i^1]){
d[v]=d[u]+f[i^1];
now[v]=first[v];
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}return d[0]!=-1;
}
int dfs(int x,int flow){
if(x==T) return flow;
int rest=flow,i;
vis[x]=1;
for(i=now[x];i&&rest;i=nxt[i]){
int v=to[i];
if(!vis[v]&&w[i]&&d[v]==d[x]-f[i]){
int k=dfs(v,min(rest,w[i]));
if(k==0){d[v]=0;continue;}
w[i]-=k,w[i^1]+=k,rest-=k,ret+=k*f[i];
}
}vis[x]=0,now[x]=i;
return flow-rest;
}
void build(){
for(int i=1;i<=n;i++) add(i,i+n,1,a[i+n]);
for(int i=1;i<=2*n-1;i++) add(i,i+1,k,0);
for(int i=1;i<=n;i++) add(2*n+1,i,1,a[i]);
for(int i=n+1;i<=2*n;i++) add(i,2*n+2,1,a[i+n]);
add(0,2*n+1,k,0),add(2*n+2,T,k,0);
}
int zkw(){
int ans=0,fl=0;
while(bfs()){
memset(vis,0,sizeof(vis));
while(fl=dfs(0,inf)) ans+=fl;
}return ret;
}
int main(){
scanf("%d%d",&n,&k),T=2*n+3;
for(int i=1;i<=3*n;i++) scanf("%d",&a[i]);
build();
printf("%d\n",zkw());
return 0;
}
做题启发
1,对于多个不等式,可以引用辅助变量(如变量y)
2,对于在等式左右两边分别出现过的变量,根据网络中每个节点流入量等于流出量的性质,可以考略用网络流建模做