qwq神仙题啊
这个题有好几个建图的方式,这里只介绍其中一种
里面掺杂了个人理解,要是有不对的地方还望指出。
首先,我们将保留一个物品看成是卖出再买进。然后我们令每次都强制买进一个物品,那么如果保留一个物品,我们就看成是在当前天的前一天卖出,然后当前天再买入。
那么这样就能得出一个靠谱的建图
首先我们对于第 天,建边 流量是1,费用是 的边,表示每天都强制买入,然后我们新建一排点,表示一个中转点,就是当前天的物品是卖还是不卖,建边 ;流量是1,费用是0,表示这个物品不卖,也就是不保留,直接扔掉。
然后
流量是1,表示一个物品只能卖一次。
流量是
表示每一天可以留到下一天
本书。
如果对于一个物品,我们选择保留的话,就相当于卖出再买入,但是由于保留了,所以上一次这本书到这一次之前一天的其他书籍的数量要减一。
所以我们 流量是1,费用是
表示保留这个东西
然后直接最小费用流就ok
qwq其实还是挺神仙,代码里面也有讲解的。
qwq
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 510;
const int maxm = 2e6+1E2;
const int inf = 1e9;
int point[maxn],nxt[maxm],to[maxm];
int flow[maxm],pre[maxm],cost[maxm];
int from[maxn],vis[maxn],dis[maxn];
int cnt=1,n,m;
int pos[2000100];
int a[maxn],w[maxn];
int ans;
int s,t;
void addedge(int x,int y,int w,int f)
{
nxt[++cnt]=point[x];
to[cnt]=y;
pre[cnt]=x;
cost[cnt]=w;
flow[cnt]=f;
point[x]=cnt;
}
void insert(int x,int y,int w,int f)
{
addedge(x,y,w,f);
addedge(y,x,-w,0);
}
queue<int> q;
bool spfa(int s)
{
memset(vis,0,sizeof(vis));
memset(dis,127/3,sizeof(dis));
dis[s]=0;
q.push(s);
while (!q.empty())
{
int x=q.front();
q.pop();
vis[x]=0;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (flow[i]>0 && dis[p]>dis[x]+cost[i])
{
dis[p]=dis[x]+cost[i];
from[p]=i;
if(!vis[p])
{
q.push(p);
vis[p]=1;
}
}
}
}
//cout<<"***"<<endl;
if (dis[t]>=dis[maxn-3]) return 0;
else return 1;
}
void mcf()
{
int x = inf;
for (int i=from[t];i;i=from[pre[i]])
{
x = min(x,flow[i]);
}
for (int i=from[t];i;i=from[pre[i]])
{
flow[i]-=x;
flow[i^1]+=x;
ans+=x*cost[i];
}
}
void solve()
{
while(spfa(s))
mcf();
}
int k;
int main()
{
n=read(),k=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) w[i]=read();
s=maxn-10;
t=s+1;
for (int i=1;i<=n;i++)
{
insert(s,i,w[a[i]],1);
insert(i,i+n,0,1); //这个边表示买下这个东西 然后立刻扔掉
insert(i+n,t,0,1);
if (pos[a[i]]) insert(i-1,pos[a[i]]+n,-w[a[i]],1);//表示把上次购买的这个物品留到现在,然后这一段之间的留下的书的数量减一(因为从S流到pos[a[i]]的边会流下来占据一个流量)
pos[a[i]]=i;
}
for (int i=1;i<n;i++) insert(i,i+1,0,k-1);
solve();
cout<<ans;
return 0;
}