非常巧妙的构图题。
建模方法:
将每一天拆成两个点i,i',加下列边
(s,i,ri,p)——在第i天可以买至多ri个餐巾,每块p分
(i,t,ri,0)——在第i天要用ri块餐巾
(s,i',ri,0)——在第i天用剩的ri块旧餐巾
(i',i+m,inf,f)——第i天的旧餐巾送到快洗部,每块f分
(i',i+n,inf,s)——第i天的旧餐巾送到慢洗部,每块s分
(i',i'+1,inf,0)——第i天的旧餐巾可以留到第i+1天再处理
——Edelweiss
最后再做一次费用流即可。
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 2000 + 10;
const int maxm = 400000;
struct Edge
{
int pos,d,c,w;
int next;
}E[maxm];
int head[maxn];
int NE = 0,s,t,nodenum;
int dis[maxn],pre[maxn],que[maxm];
bool vis[maxn];
int N,p,n,m,f,k;
void init()
{
freopen("name1745.in","r",stdin);
freopen("name1745.out","w",stdout);
}
void insert(int u,int v,int c,int w)
{
E[NE].c = c;E[NE].w = w;E[NE].pos = v;E[NE].d = u;
E[NE].next = head[u];head[u] = NE++;
E[NE].c = 0;E[NE].w = -w;E[NE].pos = u;E[NE].d = v;
E[NE].next = head[v];head[v] = NE++;
}
void readdata()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d%d%d",&N,&p,&m,&f,&n,&k);
s = 0;t = 2 * N + 1;
nodenum = t + 1;
for(int i = 1;i <= N;i++)
{
int tmp;
scanf("%d",&tmp);
insert(s,i,tmp,p);
insert(i,t,tmp,0);
insert(s,i + N,tmp,0);
if(i + m <= N)insert(i + N,i + m,inf,f);
if(i + n <= N)insert(i + N,i + n,inf,k);
if(i + 1 <= N)insert(i + N,i + N + 1,inf,0);
}
}
bool spfa()
{
memset(pre,-1,sizeof(pre));
memset(vis,false,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
dis[s] = 0;
int l = 0,r = 0;
que[r++] = s;
vis[s] = true;
while(l < r)
{
int u = que[l++];
vis[u] = false;
for(int i = head[u];i != -1;i = E[i].next)
{
int v = E[i].pos;
if(E[i].c && dis[u] + E[i].w < dis[v])
{
dis[v] = dis[u] + E[i].w;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
que[r++] = v;
}
}
}
}
if(dis[t] == inf)return false;
else return true;
}
int MCMF()
{
int ret = 0,flow = 0;
while(spfa())
{
int u = t;
int min = inf;
while(u != s)
{
if(E[pre[u]].c < min)min = E[pre[u]].c;
u = E[pre[u]].d;
}
flow += min;
u = t;
while(u != s)
{
E[pre[u]].c -= min;
E[pre[u]^1].c += min;
u = E[pre[u]].d;
}
ret += min * dis[t];
}
return ret;
}
void solve()
{
printf("%d\n",MCMF());
}
int main()
{
init();
readdata();
solve();
return 0;
}