网络流24题 餐巾计划问题 解题报告
餐巾计划问题
这建图太厉害了,我想了巨久都没想到怎么控制流量先合法又能流到别的地方去。
还是思维太烂了qaq
用了一个把流量拆开的思路,就叫Ta拆流吧
具体的说,我们对一个点,有一个流量一定要流,但是这个流量也可以给后面的用,我们就把这个流拆开。
对应此题,先把点拆成\(i\)和\(i+n\),然后\(i\)连接\(t\)表示流量限制,费用为\(0\),要流满,这也是整个图唯一连接\(t\)的边。
然后\(i+n\)表示当天用完的这个脏餐巾的流,这个流可以给下一天的脏餐巾,可以送去快洗慢洗再给对应的\(j\)号点代表\(j\)得到流
然后建一下买餐巾的流就可以了
Code:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <queue>
#include <algorithm>
using std::min;
#define ll long long
template <class T>
void read(T &x)
{
x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
const int N=4010;
const int M=1e5;
const int inf=0x3f3f3f3f;;
int head[N],to[M],Next[M],edge[M],cost[M],cnt=1;
void add(int u,int v,int w,int c)
{
to[++cnt]=v,edge[cnt]=w,cost[cnt]=c,Next[cnt]=head[u],head[u]=cnt;
to[++cnt]=u,edge[cnt]=0,cost[cnt]=-c,Next[cnt]=head[v],head[v]=cnt;
}
int n,need[N],s,t;
int used[N],dis[N],pre[N];
bool spfa()
{
memset(dis,0x3f,sizeof dis);
dis[s]=0;
std::queue <int> q;
q.push(s);
while(!q.empty())
{
int now=q.front();
q.pop();
used[now]=0;
for(int v,i=head[now];i;i=Next[i])
if(edge[i]&&dis[v=to[i]]>dis[now]+cost[i])
{
dis[v]=dis[now]+cost[i];
pre[v]=i;
if(!used[v]) q.push(v),used[v]=1;
}
}
return dis[t]<dis[0];
}
int main()
{
read(n);
for(int i=1;i<=n;i++) read(need[i]);
int t1,t2,p0,p1,p2;
read(p0),read(t1),read(p1),read(t2),read(p2);
s=n<<1|1,t=s+1;
for(int i=1;i<=n;i++)
{
add(s,i,inf,p0);
add(i,t,need[i],0);
add(s,i+n,need[i],0);
if(i<n) add(i+n,i+1+n,inf,0);
if(i+t1<=n) add(i+n,i+t1,inf,p1);
if(i+t2<=n) add(i+n,i+t2,inf,p2);
}
ll ans=0;
while(spfa())
{
int mi=inf,now=t;
while(pre[now])
{
mi=min(mi,edge[pre[now]]);
now=to[pre[now]^1];
}
ans+=mi*dis[t];now=t;
while(pre[now])
{
edge[pre[now]]-=mi;
edge[pre[now]^1]+=mi;
now=to[pre[now]^1];
}
}
printf("%lld\n",ans);
return 0;
}
2019.2.26