CF802C Heidi and Library (hard) 最小费用流

你有一个容量为k的空书架,现在共有n个请求,每个请求给定一本书ai,如果你的书架里没有这本书,你就必须以ci的价格购买这本书放入书架。

当然,你可以在任何时候丢掉书架里的某本书。请求出完成这n个请求所需要的最少价钱。

做法1:

把每个请求拆成两个点 A,B

A表示买入 B表示卖出

addedge(S,A,1,c[a[i]]) addedge(A,B,1,0) addedge(B,T,1,0) 这样最大流必是n

每天的A向下一天的A连流量为k-1,费用为0的边,表示可以不扔,留到明天,但明天的书还需要一个位置,所以是k-1

每天的前一天向上一个书出现的位置的B连一条费用为c[a[i]],流量为1的边,表示在已经有这本书的情况下,可以卖掉这本书

跑一遍最小费用即为答案

#include <cstdio>
#include <queue>
#include <cstring>
#define INF 2147483647
using namespace std;
const int MAXN = 210;
const int MAXM = 200010;
queue <int> q; 
int s, t, now, n;
struct Edge{
    int from, next, to, rest, cost;
}e[MAXM];
int head[MAXN], num = 1, dis[MAXN], vis[MAXN], Flow[MAXN], pre[MAXN];
inline void Add(int from, int to, int flow, int cost){
    e[++num] = (Edge){ from, head[from], to, flow, cost }; head[from] = num;
    e[++num] = (Edge){ to, head[to], from, 0, -cost }; head[to] = num;
}
int RoadsExist(){
    q.push(s);
    memset(dis, 127, sizeof dis);
    dis[s] = 0; Flow[s] = INF; pre[t] = 0;
    while(!q.empty()){
      now = q.front(); q.pop(); vis[now] = 0;
      for(int i = head[now]; i; i = e[i].next)
         if(e[i].rest && dis[e[i].to] > dis[now] + e[i].cost){
           dis[e[i].to] = dis[now] + e[i].cost;
           pre[e[i].to] = i;
           Flow[e[i].to] = min(Flow[now], e[i].rest);
           if(!vis[e[i].to]){
             vis[e[i].to] = 1;
             q.push(e[i].to);
           }
         }
    }
    return pre[t];
}
int k, maxflow, mincost, sum;
int a[MAXN], c[MAXN], last[MAXN];
int main(){
    scanf("%d%d", &n, &k); s = 199; t = 200;
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i) scanf("%d", &c[i]);
    for(int i = 1; i <= n; ++i){
       Add(s, i, 1, c[a[i]]);
       if(i != 1) Add(i - 1, i, k - 1, 0);
       Add(i, i + n, 1, 0);
       if(last[a[i]]) Add(i - 1, last[a[i]] + n, 1, -c[a[i]]);
       Add(i + n, t, 1, 0);
       last[a[i]] = i;
    }
    while(RoadsExist()){
      mincost += Flow[t] * dis[t];
      for(int i = t; i != s; i = e[pre[i]].from){
         e[pre[i]].rest -= Flow[t];
         e[pre[i] ^ 1].rest += Flow[t];
      }
    }
    printf("%d\n", mincost);
    return 0;
}

 

做法2:

把每个请求拆成两个点 A,B

A表示买入 B表示卖出

addedge(S,A,1,c[a[i]]) addedge(A,B,1,-INF) addedge(B,T,1,0) 这样最大流同样是n

然后n2遍历每个点对 如果书类型相同则addedge(Bi,Aj,1,0)表示这本书可以给第j个用

类型不同则addedge(Bi,Aj,1,c[a[j]]) 表示花这么多钱买入

然后跑min(n,k)次 SPFA 得到的费用即为答案(也可以拆S点来限制流量)

为什么要跑min(n,k)次的原因我认为是对于容量为min(n,k)的书架 其实你就是要选择min(n,k)个起始书 之后寻找最短路的最优方案即为寻找换书的最优方案 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define MAX 85
const int inf=1e7;
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
namespace MCMF
{
    const int MAXM=1000000,MAXN=1000;
    struct Line{int v,next,w,fy;}e[MAXM];
    int h[MAXN],cnt=2;
    inline void Add(int u,int v,int w,int fy)
    {
        e[cnt]=(Line){v,h[u],w,fy};h[u]=cnt++;
        e[cnt]=(Line){u,h[v],0,-fy};h[v]=cnt++;
    }
    int dis[MAXN],pe[MAXN],pv[MAXN],Cost,Flow;
    bool vis[MAXN];queue<int> Que;
    int S=MAXN-2,T=MAXN-1;
    bool SPFA()
    {
        memset(dis,63,sizeof(dis));dis[S]=0;
        Que.push(S);vis[S]=true;
        while(!Que.empty())
        {
            int u=Que.front();Que.pop();
            for(int i=h[u];i;i=e[i].next)
            {
                int v=e[i].v;if(!e[i].w)continue;
                if(dis[u]+e[i].fy<dis[v])
                {
                    dis[v]=dis[u]+e[i].fy;pe[v]=i,pv[v]=u;
                    if(!vis[v])vis[v]=true,Que.push(v);
                }
            }
            vis[u]=false;
        }
        if(dis[T]>=1e9)return false;
        int flow=1e9;
        for(int i=T;i!=S;i=pv[i])flow=min(flow,e[pe[i]].w);
        for(int i=T;i!=S;i=pv[i])e[pe[i]].w-=flow,e[pe[i]^1].w+=flow;
        Flow+=flow;Cost=min(Cost,Cost+dis[T]*flow);
        return true;
    }
}
using namespace MCMF;
int n,K,a[MAX],c[MAX];
int main()
{
    n=read();K=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=n;++i)c[i]=read();
    for(int i=1;i<=n;++i)Add(S,i,1,c[a[i]]);
    for(int i=1;i<=n;++i)Add(i,i+n,1,-inf);
    for(int i=1;i<=n;++i)Add(i+n,T,1,0);
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
            Add(i+n,j,1,a[i]==a[j]?0:c[a[j]]);
    Cost+=n*inf;K=min(K,n);while(K--)SPFA();
    printf("%d\n",Cost);
    return 0;
}

 

posted @ 2019-10-24 13:39  Aragaki  阅读(118)  评论(0编辑  收藏  举报