zoukankan      html  css  js  c++  java
  • [SDOI2014]LIS(最小割)

    [SDOI2014]LIS(最小割)

    题面

    给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若干项,使得A的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。 如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。
    (n leq 700)

    分析

    先做一次LIS的DP,设(f_i)表示以(i)结尾的LIS长度,序列LIS长度为(len)。那么类似网络流24题中的最长不下降子序列问题建图

    把每个数拆成入点(i)和出点(i+n)

    1. 对于(f_i=1)的所有(i), 连边((s,i,+infin))
    2. 对于(f_i=len)的所有(i),连边((i+n,t,infin))
    3. 对于(j<i),若$a_i>a_j,f_i=f_j+1 (,连边)(j+n,i,infin)$
    4. 对于每个(i),连边((i,i+n,b_i))

    这样从(s)(t)的一条路径就代表了一个LIS,删除项就相当于割断边。最小割即为答案。

    考虑如何输出最小割方案。因为要求按(c_i)字典序最小,我们把可能的割边((i,i+n))按照(c_i)排序。接着判定,若残量网络上的边((u,v))满流,且从(u)(v)不存在其他的增广路(可以经过其他的反向边),则((u,v))是可行的割边。找到一条割边后,从(s)经过(u,v)(t)上面可能有很多条边是等价的,因此要让这些边不满流,这样就不可能成为割边。于是要把流量退回去,直接从(u)(s),(v)(t)跑Dinic即可。

    关键代码如下:

    for(int i=1;i<=n;i++){//把可能的割边按c排序 
        int k=id[i];
        int eid=finde(k,k+n);//找到邻接表里割的编号 
        if(!bfs(k,k+n)){//如果从k到k+n不存在其他的增广路,割掉这条边之后这个图才不连通 
            ans.push_back(k);
            dinic(t,k+n);//把k+1到t的流退掉,因为这一条路径上的割边是等价的,割掉这条就不需要其他的 
            dinic(k,s);//同理 
            E[eid].flow=E[eid^1].flow=0;
        }
    }	
    

    代码

    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector> 
    #include<algorithm>
    #define maxn 1400
    #define maxm (maxn*maxn)
    #define INF 0x3f3f3f3f
    using namespace std;
    using namespace std;
    typedef long long ll;
    struct edge{
        int from;
        int to;
        int next;
        int flow;
    }E[maxm*2+5];
    int head[maxn+5];
    int cur[maxn+5];
    int esz=1;
    void add_edge(int u,int v,int w){
    //	printf("%d->%d %d
    ",u,v,w); 
        esz++;
        E[esz].from=u;
        E[esz].to=v;
        E[esz].flow=w;
        E[esz].next=head[u];
        head[u]=esz;
        esz++;
        E[esz].from=v;
        E[esz].to=u;
        E[esz].flow=0;
        E[esz].next=head[v];
        head[v]=esz;
    }
    int deep[maxn+5];
    bool bfs(int s,int t){
        memset(deep,0,sizeof(deep));
        queue<int>q;
        q.push(s);
        deep[s]=1;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=E[i].next){
                int y=E[i].to;
                if(!deep[y]&&E[i].flow){
                    deep[y]=deep[x]+1;
                    q.push(y);
                    if(y==t) return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int x,int t,int minf){
        if(x==t) return minf;
        int rest=minf,k;
        for(int &i=cur[x];i;i=E[i].next){
            int y=E[i].to;
            if(E[i].flow&&deep[y]==deep[x]+1){
                k=dfs(y,t,min(rest,E[i].flow));
                E[i].flow-=k;
                E[i^1].flow+=k;
                rest-=k;
                if(k==0) deep[y]=0;
                if(rest==0) break; 
            }
        }
        return minf-rest;
    }
    ll dinic(int s,int t){
        ll ans=0;
        int now=0;
        while(bfs(s,t)){
            memcpy(cur,head,sizeof(head));
            while((now=dfs(s,t,INF))) ans+=now;
        } 
        return ans;
    }
    
    int T,n;
    int s,t;
    int a[maxn+5],b[maxn+5],c[maxn+5];
    int dp[maxn+5];
    void ini(){
        esz=1;
        memset(head,0,sizeof(head));
        memset(dp,0,sizeof(dp)); 
    }
    void build_graph(){
        int len=0;
        s=0,t=2*n+1;
        for(int i=1;i<=n;i++){
            dp[i]=1;
            for(int j=1;j<i;j++) if(a[i]>a[j]) dp[i]=max(dp[j]+1,dp[i]);
            len=max(len,dp[i]);
        }
        for(int i=1;i<=n;i++){
            add_edge(i,i+n,b[i]);
            if(dp[i]==1) add_edge(s,i,INF);
            if(dp[i]==len) add_edge(i+n,t,INF);
            for(int j=1;j<i;j++) if(a[i]>a[j]&&dp[i]==dp[j]+1) add_edge(j+n,i,INF);
        }
    }
    int finde(int x,int y){
        for(int i=head[x];i;i=E[i].next)if(E[i].to==y&&E[i].flow==0) return i;
        return 0;
    }
    
    void print_sol(){
        static int id[maxn+5];
        vector<int>ans;
        for(int i=1;i<=n;i++) id[i]=i;
        sort(id+1,id+1+n,[](int x,int y)->bool{return c[x]<c[y];});
        for(int i=1;i<=n;i++){//把可能的割边按c排序 
            int k=id[i];
            int eid=finde(k,k+n);//找到邻接表里割的编号 
            if(!bfs(k,k+n)){//如果从k到k+n不存在其他的增广路,割掉这条边之后这个图才不连通 
                ans.push_back(k);
                dinic(t,k+n);//把k+1到t的流退掉,因为这一条路径上的割边是等价的,割掉这条就不需要其他的 
                dinic(k,s);//同理 
                E[eid].flow=E[eid^1].flow=0;
            }
        }	
        printf("%d
    ",(int)ans.size());
        sort(ans.begin(),ans.end());
        for(int x:ans) printf("%d ",x);
        printf("
    ");
    } 
    int main(){
    //	freopen("input.txt","r",stdin);
        scanf("%d",&T);
        while(T--){
            ini(); 
            scanf("%d",&n);
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            for(int i=1;i<=n;i++) scanf("%d",&b[i]);
            for(int i=1;i<=n;i++) scanf("%d",&c[i]);
            build_graph();
            printf("%lld ",dinic(s,t));
            print_sol(); 
        }
    } 
    /*
    1
    5
    6 5 8 7 3 
    8 8 2 8 5 
    1 4 2 5 3 
    */
    
  • 相关阅读:
    如何构建一个优秀的移动网站?谷歌专家教你25招(三)[转]
    如何构建一个优秀的移动网站?谷歌专家教你25招(二)[转]
    如何构建一个优秀的移动网站?谷歌专家教你25招(一)[转]
    如何做到Zero Downtime重启Go服务?
    Go语言AST尝试
    Database Go and JSON
    "创业"半年
    打造完美的go开发环境
    [ReactiveCocoa]最简单的RAC入门操作
    [Node.js]expressjs简单测试连接mysql
  • 原文地址:https://www.cnblogs.com/birchtree/p/13403945.html
Copyright © 2011-2022 走看看