zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ",Cost);
        return 0;
    }
  • 相关阅读:
    修改linux的ssh默认端口号22的方法
    centos6 配置静态IP
    homebrew 无法安装提示不能在根目录下使用
    MySQL Replication 详解MySQL数据库设置主从同步的方法
    两台Mysql数据库数据同步实现
    MySql主从同步和延迟同步
    c#委托和事件
    C# Out和Ref区别
    MVC与三层架构
    经典数据库题
  • 原文地址:https://www.cnblogs.com/Aragaki/p/11731709.html
Copyright © 2011-2022 走看看