/* 不要低头,不要放弃,不要气馁,不要慌张 题意: 给两行n个数,要求从第一行选取a个数,第二行选取b个数使得这些数加起来和最大。 限制条件是第一行选取了某个数的条件下,第二行不能选取对应位置的数。 思路: 比赛的时候一直在想如何dp。没有往网络流的方向多想想。赛后看到tag想了想,咦,费用流可做。 所以思路是最小费用最大流,dp如今都不知如何做。 将一个位置拆分成3个点,从超级源点分别到1号点连容量为a,价值为0 的边,往2号点连容量为b,价值为0的边。 对于每个位置,从1号点和2号点分别向每个位置拆分出的第一个点连一条容量为1,价值为0的边。然后从该点分别向其它两个点连容量为1,价值为 给定的点,然后这两个点均向超级汇点连接一条容量为1价值为0的边。 跑一下最小费用最大流。 */ #include<stdio.h> #include<queue> #define MAXN 55000 #define MAXM 20002*5 #define INF 0x3f3f3f3f using namespace std; int a[MAXN],b[MAXN]; int n; //起点编号必须最小,终点编号必须最大 bool vis[MAXN]; //spfa中记录是否在队列里边 struct edge{ edge *next,*op; //op是指向反向边 int t,c,v; //t下一个点编号,c容量,v权值 }ES[MAXM],*V[MAXN]; //ES边静态邻接表,V点的编号 int N,M,S,T,EC=-1; //S源点最小,T汇点最大,EC当前边数 int demond[MAXN],sp[MAXN],prev[MAXN]; //spSPFA中记录距离,prev记录上一个点路径 edge *path[MAXN]; //与prev同步记录,记录到上一条边 void addedge(int a,int b,int v,int c=INF){ edge e1={V[a],0,b,c,v},e2={V[b],0,a,0,-v}; ES[++EC]=e1;V[a]=&ES[EC]; ES[++EC]=e2;V[b]=&ES[EC]; V[a]->op=V[b];V[b]->op=V[a]; } void init(){ int num1,num2; scanf("%d%d%d",&n,&num1,&num2); for(int i=1;i<=n;i++)scanf("%d",a+i); for(int i=1;i<=n;i++)scanf("%d",b+i); S=0;T=3*n+3; EC=-1; for(int i=1;i<=n;i++){ addedge(S,i,0,1); addedge(i,n+i,-a[i],1); addedge(i,n*2+i,-b[i],1); addedge(n+i,T-2,0,1); addedge(n*2+i,T-1,0,1); } addedge(T-2,T,0,num1); addedge(T-1,T,0,num2); } bool SPFA(){ int u,v; for(u=S;u<=T;u++){ sp[u]=INF; } queue<int>q; prev[S]=-1; q.push(S); sp[S]=0; vis[S]=1; while(!q.empty()){ u=q.front(); vis[u]=0; q.pop(); for(edge *k=V[u];k;k=k->next){ v=k->t; if(k->c>0&&sp[u]+k->v<sp[v]){ sp[v]=sp[u]+k->v; prev[v]=u; path[v]=k; if(vis[v]==0){ vis[v]=1; q.push(v); } } } } return sp[T]!=INF; } int argument(){ int i,cost=INF,flow=0; edge *e; for(i=T;prev[i]!=-1;i=prev[i]){ e=path[i]; if(e->c<cost)cost=e->c; } for(int i=T;prev[i]!=-1;i=prev[i]){ e=path[i]; e->c-=cost;e->op->c+=cost; flow+=e->v*cost; } return flow; } int maxcostflow(){ int Flow=0; while(SPFA()){ Flow+=argument(); } return Flow; } int main(){ init(); printf("%d ",-maxcostflow()); for(int i=1;i<=n;i++){ for(edge *it=V[i+n];it;it=it->next){ if(it->t<=n&&it->c>0)printf("%d ",i); } } puts(""); for(int i=1;i<=n;i++){ for(edge *it=V[i+2*n];it;it=it->next){ if(it->t<=n&&it->c)printf("%d ",i); } } return 0; }