CF802C Heidi and Library (hard) (网络流+最大流)

qwq神仙题啊

这个题有好几个建图的方式,这里只介绍其中一种

里面掺杂了个人理解,要是有不对的地方还望指出。

首先,我们将保留一个物品看成是卖出再买进。然后我们令每次都强制买进一个物品,那么如果保留一个物品,我们就看成是在当前天的前一天卖出,然后当前天再买入。

那么这样就能得出一个靠谱的建图

首先我们对于第 i i 天,建边 s > i s->i 流量是1,费用是 w [ a [ i ] ] w[a[i]] 的边,表示每天都强制买入,然后我们新建一排点,表示一个中转点,就是当前天的物品是卖还是不卖,建边 i > i + n i->i+n ;流量是1,费用是0,表示这个物品不卖,也就是不保留,直接扔掉。

然后 i + n > t i+n->t 流量是1,表示一个物品只能卖一次。
i > i + 1 i->i+1 流量是 k 1 k-1 表示每一天可以留到下一天 k 1 k-1 本书。

如果对于一个物品,我们选择保留的话,就相当于卖出再买入,但是由于保留了,所以上一次这本书到这一次之前一天的其他书籍的数量要减一。

所以我们 i 1 > p o s [ a [ i ] ] i-1 - > pos[a[i]] 流量是1,费用是 w [ a [ i ] ] -w[a[i]]

表示保留这个东西

然后直接最小费用流就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;
}

猜你喜欢

转载自blog.csdn.net/y752742355/article/details/87362470