CF802C Heidi and Library(hard)

题意:一共有n天每天一个请求,要求借一本书,即刻归还。图书馆中最多存k本书,多了就要扔掉。第i本书的价格为c[i]。问满足所有请求的最小买书花费?

n<=200。

标程:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=205;
 4 const int inf=0x3f3f3f3f;
 5 struct node{int to,next,w,c;}num[N*10];
 6 queue<int> q;
 7 int cnt=1,sc,S,T,dis[N],head[N],n,a[N],c[N],ans,k,pre[N],prevv[N],preve[N],t[1000005];
 8 void add(int x,int y,int w,int c)
 9 {num[++cnt].to=y;num[cnt].next=head[x];num[cnt].w=w;num[cnt].c=c;head[x]=cnt;
10 num[++cnt].to=x;num[cnt].next=head[y];num[cnt].w=-w;num[cnt].c=0;head[y]=cnt;}
11 
12 void build()
13 {
14     for (int i=1;i<n;i++) add(i,i+1,0,k-1);
15     for (int i=1;i<=n;i++)
16       if (pre[i]&&pre[i]!=i-1)
17       {
18             add(S,pre[i]+1,0,1);
19             add(pre[i]+1,++sc,0,1);
20             add(i,sc,-c[a[i]],1);
21             add(sc,T,0,1);
22       }
23 }
24 void spfa()
25 {
26     q.push(S);memset(dis,inf,sizeof(dis));
27     dis[S]=0;
28     while (!q.empty())
29     {
30         int now=q.front();q.pop();
31         for (int i=head[now];i;i=num[i].next)
32           if (num[i].c&&dis[num[i].to]>dis[now]+num[i].w)
33           {
34                 dis[num[i].to]=dis[now]+num[i].w,q.push(num[i].to);
35                 prevv[num[i].to]=now;preve[num[i].to]=i;
36           }
37             
38     }
39 }
40 void solve()
41 {
42     while (1==1)
43     {
44        spfa();
45        if (dis[T]==inf) break;
46        int sum=0;
47        for (int i=T,it;i!=S;i=prevv[i]) 
48           it=preve[i],sum+=num[it].w,num[it].c--,num[it^1].c++;
49        ans+=sum;
50     } 
51 }
52 int main()
53 {
54     scanf("%d%d",&n,&k);
55     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
56     for (int i=1;i<=n;i++) scanf("%d",&c[i]); 
57     for (int i=1;i<=n;i++) 
58     {
59       pre[i]=t[a[i]];t[a[i]]=i;
60       if (i==1||pre[i]!=i-1) ans+=c[a[i]]; 
61     }
62     S=n+1;T=sc=S+1;
63     build();solve();
64     printf("%d
",ans); 
65     return 0;
66 }

题解:最小费用最大流

考虑如果k=1,那么如果相邻两天的请求不相同就要重新买书,否则就可以沿用。先统计出这一部分的贡献。

再考虑k>1还可以省下多少钱。对于表示天数的点连边如下:

1. i和i+1连边,容量为k-1,费用为0。表示在第i天和第i+1天之间,某本书借出去的时候,图书馆中此时最多有k-1本书。

2.如果第i天request的书和第pre[i]天一样,那么有一种选择是pre[i]天的书存储下来。

建立辅助节点j。连边S->pre[i]+1,pre[i]+1->j,j->T,容量为1,费用为0。如果流这一条路径,相当于不保存。S->pre[i]+1,则表示第pre[i]天request的书还回来后从第pre[i]+1天开始存在书架中。

连边i->j,容量为1,费用为-c[a[i]],如果流经这条边,说明这一天的书是之前保存下来的,不用重新买。

注意如果i和i+1的书类是一样的,那么就不用连边pre(之前就没有统计进去)。

跑最小费用流即可。

原文地址:https://www.cnblogs.com/Scx117/p/9090671.html