很好的一道题,对理解最小割有很大帮助
首先,不难发现本题与网络流24题中的某一道很类似,我们可以先跑一次dp求出每个节点的LIS,然后拆点,拆出的两点之间连流量为删除的代价的边,剩下的点之间按dp的转移连流量正无穷的边,最后跑最小割即为第一问答案
但是第二问有个问题:又引入了一个量要求最小割字典序最小,这怎么办?
首先我们考虑:字典序最小的话我们就要让第一个尽可能小,然后让第二个尽可能小...以此类推
那么我们考虑什么样的边可能在最小割中
这样的边一定满足如下条件:
第一.这条边已经满流了
第二.这条边的两端之间不存在增广路
如果一条边满足这样的条件,那么这条边就可以在一个最小割之中
那么我们把点按第三个变量排序,从小到大判断能否加入最小割,如果能加入最小割的话则需要退流,也就是退掉源点和汇点到这条边两端的流,这一步可以反向dfs解决
于是这题就结束了
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; const int inf=0x3f3f3f3f; struct Edge { int nxt; int to; int val; int flo; }edge[500005]; struct Info { int num,v,w,c; friend bool operator < (Info a,Info b) { return a.c<b.c; } }f[1005]; int re[1005]; int head[5005]; int cur[5005]; int dis[5005]; int toe[5005]; int dp[1005]; int cnt=1,ttop=0; int T,n; int st,ed; void init() { memset(head,0,sizeof(head)); memset(dp,0,sizeof(dp)); cnt=1,ttop=0; } void add(int l,int r,int w) { edge[cnt].nxt=head[l]; edge[cnt].to=r; edge[cnt].val=w; head[l]=cnt++; } void dadd(int l,int r,int w) { add(l,r,w),add(r,l,0); } int ide(int x) { return x&1?x+1:x-1; } bool bfs(int fr,int Ed) { memcpy(cur,head,sizeof(head)); memset(dis,0,sizeof(dis)); dis[fr]=1; queue <int> M; M.push(fr); while(!M.empty()) { int u=M.front(); M.pop(); for(int i=head[u];i;i=edge[i].nxt) { int to=edge[i].to; if(edge[i].val>edge[i].flo&&!dis[to]) { dis[to]=dis[u]+1,M.push(to); if(to==Ed)return 1; } } } return dis[Ed]; } int dfs(int x,int lim,int Ed) { if(x==Ed||!lim)return lim; int ret=0; for(int i=cur[x];i;i=edge[i].nxt) { cur[x]=i; int to=edge[i].to; if(dis[to]==dis[x]+1&&edge[i].val>edge[i].flo) { int temp=dfs(to,min(lim,edge[i].val-edge[i].flo),Ed); if(temp) { ret+=temp,lim-=temp; edge[i].flo+=temp,edge[ide(i)].flo-=temp; if(!lim)break; } } } return ret; } int dinic() { int ans=0; while(bfs(st,ed))ans+=dfs(st,inf,ed); return ans; } inline int read() { int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { T=read(); while(T--) { init(); n=read(); st=2*n+1,ed=2*n+2; for(int i=1;i<=n;i++)f[i].num=i,f[i].v=read(),dp[i]=1; for(int i=1;i<=n;i++)f[i].w=read(),dadd((i<<1)-1,i<<1,f[i].w),toe[i]=cnt-1; for(int i=1;i<=n;i++)for(int j=1;j<i;j++)if(f[i].v>f[j].v)dp[i]=max(dp[i],dp[j]+1); int maxx=0; for(int i=1;i<=n;i++)maxx=max(maxx,dp[i]); for(int i=1;i<=n;i++) { if(dp[i]==1)dadd(st,(i<<1)-1,inf); else if(dp[i]==maxx)dadd(i<<1,ed,inf); for(int j=1;j<i;j++)if(f[i].v>f[j].v&&dp[j]+1==dp[i])dadd(j<<1,(i<<1)-1,inf); } for(int i=1;i<=n;i++)f[i].c=read(); printf("%d ",dinic()); sort(f+1,f+n+1); for(int i=1;i<=n;i++) { int k=f[i].num; if(bfs((k<<1)-1,k<<1))continue; re[++ttop]=k; while(bfs(ed,(k<<1)))dfs(ed,inf,(k<<1)); while(bfs((k<<1)-1,st))dfs((k<<1)-1,inf,st); edge[toe[k]].val=edge[ide(toe[k])].val=0; edge[toe[k]].flo=edge[ide(toe[k])].flo=0; } printf("%d ",ttop); sort(re+1,re+ttop+1); for(int i=1;i<=ttop;i++)printf("%d ",re[i]); printf(" "); } return 0; }