• 【BZOJ3532】LIS(SDOI2014)-最小割+贪心+退流


    测试地址:LIS
    做法:本题需要用到最小割+贪心+退流。
    如果仅仅是求最小代价,就是很经典的模型了,令f(i)为以第i个元素结尾的LIS长度,那么如果有i<j,ai<aj并且f(i)+1=f(j),就从ij连一条边,那么每条从一个f(i)=1的点到一个f(i)=maxfmaxf就是整个序列的LIS长度)的路径都代表着一个LIS。问题就变成了求最小权点割集,直接用经典的最小割模型拆点转化一下即可。
    现在的问题是要求字典序最小的解,我们显然可以贪心,如果一条字典序较小的点可以割,就马上割掉,肯定是最优的。因此我们按字典序顺序考虑那些由点转化成的边,这条边可能在最小割中当且仅当它满流,并且在残余网络中不存在从这条边的起点到这条边的终点的路径。那么当我们选择割一条边,为了消除掉这条边的影响,我们要把这条边的流量退掉,也就是从起点到源点退流(类似最大流的做法),从汇点到终点退流,然后把这条边删掉即可。这样我们就可以通过此题了。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll inf=1000000000ll*100000000ll;
    int Test,n,dp[710],S,T,first[2010],tot,cur[2010],lvl[2010];
    int h,t,q[2010];
    ll a[710],b[710],firstans;
    bool chosen[710],vis[2010];
    struct point
    {
        int id;
        ll c;
    }p[710];
    struct edge
    {
        int v,next;
        ll f;
    }e[1000010];
    
    bool cmp(point a,point b)
    {
        return a.c<b.c;
    }
    
    void insert(int a,int b,ll f)
    {
        e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,first[a]=tot;
        e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,first[b]=tot;
    }
    
    void init()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++)
            scanf("%lld",&b[i]);
        for(int i=1;i<=n;i++)
        {
            p[i].id=i;
            scanf("%lld",&p[i].c);
        }
    
        int maxd=0;
        for(int i=1;i<=n;i++)
        {
            dp[i]=1;
            for(int j=1;j<i;j++)
                if (a[j]<a[i]) dp[i]=max(dp[j]+1,dp[i]);
            maxd=max(maxd,dp[i]);
        }
    
        memset(first,0,sizeof(first));
        tot=1;
        S=(n<<1)+1,T=(n<<1)+2;
        for(int i=1;i<=n;i++)
            insert(i,n+i,b[i]);
        for(int i=1;i<=n;i++)
        {
            if (dp[i]==1) insert(S,i,inf);
            if (dp[i]==maxd) insert(n+i,T,inf);
            for(int j=i+1;j<=n;j++)
                if (a[i]<a[j]&&dp[i]+1==dp[j]) insert(n+i,j,inf);
        }
    }
    
    bool makelevel(int S,int T)
    {
        for(int i=1;i<=(n<<1)+2;i++)
            lvl[i]=-1,cur[i]=first[i];
        h=t=1;
        q[1]=S;
        lvl[S]=0;
        while(h<=t)
        {
            int v=q[h++];
            for(int i=first[v];i;i=e[i].next)
                if (e[i].f&&lvl[e[i].v]==-1)
                {
                    lvl[e[i].v]=lvl[v]+1;
                    q[++t]=e[i].v;
                }
        }
        return lvl[T]!=-1;
    }
    
    ll maxflow(int v,int T,ll maxf)
    {
        ll ret=0,f;
        if (v==T) return maxf;
        for(int i=cur[v];i;i=e[i].next)
        {
            if (e[i].f&&lvl[e[i].v]==lvl[v]+1)
            {
                f=maxflow(e[i].v,T,min(maxf-ret,e[i].f));
                ret+=f;
                e[i].f-=f;
                e[i^1].f+=f;
                if (ret==maxf) break;
            }
            cur[v]=i;
        }
        if (!ret) lvl[v]=-1;
        return ret;
    }
    
    void dinic(int S,int T,ll limit,bool type)
    {
        ll maxf=0;
        while(makelevel(S,T))
        {
            maxf+=maxflow(S,T,limit-maxf);
            if (maxf==limit) break;
        }
        if (type) printf("%lld ",firstans=maxf);
    }
    
    void work()
    {
        sort(p+1,p+n+1,cmp);
        memset(chosen,0,sizeof(chosen));
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            int v=p[i].id;
            if (e[v<<1].f) continue;
            if (makelevel(v,n+v)) continue;
    
            chosen[v]=1;
            dinic(v,S,b[v],0);
            dinic(T,n+v,b[v],0);
            e[v<<1|1].f=0;
            firstans-=b[v];
    
            cnt++;
            if (!firstans) break;
        }
        printf("%d
    ",cnt);
        for(int i=1;i<=n;i++)
            if (chosen[i]) printf("%d ",i);
        printf("
    ");
    }
    
    int main()
    {
        scanf("%d",&Test);
        while(Test--)
        {
            init();
            dinic(S,T,inf,1);
            work();
        }
    
        return 0;
    }
  • 相关阅读:
    力扣算法题—091解码
    力扣算法题—090子集2
    力扣算法题—089格雷编码
    力扣算法题—088合并两个有序数组
    HDU 1509 Windows Message Queue
    HDU 1241 Oil Deposits
    HDU 1096 A+B for Input-Output Practice (VIII)
    HDU 1108 最小公倍数
    HDU 1106 排序
    HDU 1003 Max Sum * 最长递增子序列(求序列累加最大值)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793392.html
Copyright © 2020-2023  润新知