【BZOJ3532】LIS(SDOI2014)-最小割+贪心+退流
测试地址:LIS
做法:本题需要用到最小割+贪心+退流。
如果仅仅是求最小代价,就是很经典的模型了,令为以第个元素结尾的LIS长度,那么如果有并且,就从向连一条边,那么每条从一个的点到一个(就是整个序列的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\n",cnt);
for(int i=1;i<=n;i++)
if (chosen[i]) printf("%d ",i);
printf("\n");
}
int main()
{
scanf("%d",&Test);
while(Test--)
{
init();
dinic(S,T,inf,1);
work();
}
return 0;
}