【费用流】洛谷1251 餐巾计划问题
今年参加BCPC暨ACM选拔的同学非常多。看了下预赛榜单,my和ljy率领一大批18级dalao,tky等buaacoding刷题狂魔也都参加了,更可怕的是还有一堆CF黄名橙名故意只写了一点点题隐藏实力。这对于你航ACM事业当然是好事,但对于我们这些菜鸡就很黑暗了。希望明天能加油,Alchemist加油~
不废话了,来看这题。如果不告诉我是网络流,我估计还真的很难想到它的算法。但告诉我是网络流之后,构图依旧不是很容易。一开始想着,拿每一天当边,拿快洗,慢洗,等待,当做状态点;后来又想到每一天的拆点——白天和晚上,超级源点连接白天供应新餐巾,然后两点间连接r[i]的边,快洗慢分别从某天晚上到另一天上午,延迟等待则从某天晚上到下一天晚上,这样连边,然后跑费用流,求出最小费用就是答案了。看起来很美好也很合理,但是实际上跑的时候就发现不对劲了。费用流首先要满足最大流。最大流此时显然是所用餐巾数的总和,此时每天的餐巾使用量才能达到要求。
然而,发现容量为r[i]的边满流时,可能此时不是最大流,但是此时费用更小。也就是可能不是最大流依旧满足题意。但是费用流要强制变成最大流,这可为之奈何?
无奈之下看了题解的构图方式,现在还没完全理解,感觉还是有些玄妙的,说不出具体的含义。叙述如下
1.超级源点连接白天点,权值为INF,费用为p
2.白天点连接超级汇点,权值为r[i],费用为0
3.超级源点连接黑夜点,权值为r[i],费用为0
4.黑夜点连接下一天黑夜点,权值为INF,费用为0
5.黑夜点连向m天之后的快洗边,权值为INF,费用为f
6.黑夜点连向n天之后的慢洗边,权值为INF,费用为s
大概可以这样想,首先,如果完全不考虑旧的餐巾,只有新的餐巾,显然只有1和2两类边。现在考虑旧餐巾,则5和6为原来的新餐巾提供新的来源,做出旧餐巾的替换。然后呢,因为每天产生或者提供的旧餐巾也是有上限的,所以就有了3。最后考虑旧餐巾的传递问题,就有了4。(好吧其实我没完全想明白)
MCMF我又用的是汝佳的模板~
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
using LL=long long;
const int maxn=4005;
struct Edge
{
int from,to;
LL cap,flow,cost;
};
struct MCMF
{
int n,m,s,t;
vector<Edge> edges;
vector<int> G[maxn];
bool inq[maxn];
int p[maxn];
LL d[maxn],a[maxn];
void init(int n)
{
this->n=n;
for(int i=0;i<n;i++)
G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap, int cost)
{
edges.push_back((Edge){from,to,cap,0,cost});
edges.push_back((Edge){to,from,0,0,-cost});
m=edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int s, int t, LL &flow, LL &cost)
{
for(int i=0;i<n;i++)
d[i]=1E18,inq[i]=false;
d[s]=0,inq[s]=true,p[s]=0,a[s]=1E18;
queue<int> Q;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();Q.pop();
inq[u]=false;
for(int i=0;i<G[u].size();i++)
{
Edge &e=edges[G[u][i]];
if(e.cap>e.flow&&d[e.to]>d[u]+e.cost)
{
d[e.to]=d[u]+e.cost;
p[e.to]=G[u][i];
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to])
{
Q.push(e.to);
inq[e.to]=true;
}
}
}
}
if(d[t]==1E18)
return false;
flow+=a[t];
cost+=d[t]*a[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
return true;
}
LL Mincost(int s, int t)
{
LL flow=0,cost=0;
while(BellmanFord(s,t,flow,cost))
printf("");
return cost;
}
}X;
int N,r[2005],p,m,f,n,s;
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d",&r[i]);
scanf("%d%d%d%d%d",&p,&m,&f,&n,&s);
X.init(2*N+2);
for(int i=1;i<=N;i++)
{
X.AddEdge(0,i,r[i],0);
X.AddEdge(i+N,2*N+1,r[i],0);
}
for(int i=1;i<=N;i++)
{
X.AddEdge(0,i+N,1E9,p);
if(i+m<=N)
X.AddEdge(i,i+N+m,1E9,f);
if(i+n<=N)
X.AddEdge(i,i+n+N,1E9,s);
if(i+1<=N)
X.AddEdge(i,i+1,1E9,0);
}
printf("%lld",X.Mincost(0,2*N+1));
return 0;
}