【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;
}
原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793392.html