题意:
给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若
干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。 如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。
1 < =N < =700
T < =5
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 1500
#define LL long long
using namespace std;
struct node{int y,nex;LL c;}a[N*N];
struct node1{int d,id;}C[N];
int A[N],B[N],n,len,fir[N],f[N],w[N],res[N],cnt,st,ed,tail,p[N],h[N];
LL inf=1ll<<50;
bool b[N];
void ins(int x,int y,LL c)
{
a[++len].y=y;a[len].c=c;a[len].nex=fir[x];fir[x]=len;
a[++len].y=x;a[len].c=0;a[len].nex=fir[y];fir[y]=len;
}
bool cmp(node1 x,node1 y)
{
if(x.d<y.d) return 1;
return 0;
}
bool bfs()
{
for(int i=1;i<=2*n+2;i++) h[i]=0;
p[1]=st;h[st]=1;tail=1;
for(int i=1;i<=tail;i++)
{
int x=p[i];
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(a[k].c && h[y]==0)
{
h[y]=h[x]+1;p[++tail]=y;
}
}
}
return h[ed];
}
LL dfs(int x,LL flow)
{
LL t,dlt=0;
if(x==ed) return flow;
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(dlt==flow) break;
if(a[k].c && h[y]==h[x]+1) {t=dfs(y,min(flow-dlt,a[k].c));dlt+=t;a[k].c-=t;a[k^1].c+=t;}
}
if(dlt==0) h[x]=0;
return dlt;
}
void flow_back(int x,int y,LL c)
{
st=x;ed=y;
while(bfs() && c)
c-=dfs(st,c);
}
int main()
{
int z;scanf("%d",&z);
while(z--)
{
for(int i=1;i<=2*n+2;i++) fir[i]=0;
len=1;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&A[i]);
for(int i=1;i<=n;i++) scanf("%d",&B[i]);
for(int i=1;i<=n;i++) scanf("%d",&C[i].d),C[i].id=i;
int mx=0;
for(int i=n;i>=1;i--)
{
f[i]=0;
for(int j=i+1;j<=n;j++) if(A[j]>A[i]) f[i]=max(f[i],f[j]);
f[i]++;
mx=max(mx,f[i]);
}
st=2*n+1;ed=st+1;
for(int i=1;i<=n;i++) b[i]=0;
for(int i=1;i<=n;i++) w[i]=len+1,ins(i,i+n,B[i]);
for(int i=1;i<=n;i++)
{
if(f[i]==mx) b[i]=1;
if(b[i]==0) continue;
for(int j=i+1;j<=n;j++) if(A[j]>A[i] && f[j]==f[i]-1) b[j]=1;
}
for(int i=1;i<=n;i++)
{
if(b[i]==0) continue;
if(f[i]==mx) ins(st,i,inf);
if(f[i]==1) ins(i+n,ed,inf);
for(int j=i+1;j<=n;j++) if(A[j]>A[i] && f[j]==f[i]-1) ins(i+n,j,inf);
}
LL mxf=0;
while(bfs()) mxf+=dfs(st,inf);
printf("%lld ",mxf);
sort(C+1,C+n+1,cmp);
cnt=0;
for(int i=1;i<=n;i++)
{
int k=w[C[i].id];
if(a[k].c) continue;
int x=a[k^1].y,y=a[k].y;
st=x;ed=y;
if(bfs()) continue;
res[++cnt]=C[i].id;
flow_back(x,2*n+1,a[k^1].c);
flow_back(2*n+2,y,a[k^1].c);
a[k^1].c=0;
}
printf("%d\n",cnt);
sort(res+1,res+cnt+1);
for(int i=1;i<cnt;i++) printf("%d ",res[i]);
printf("%d\n",res[cnt]);
}
return 0;
}
题解:
拆点最小割是显然的,然而字典序就不会做了QAQ
膜了题解,看到一种退流的方法
做完网络流后,贪心地尽量让c小的边成为割边
是否能做为割边就看小姿势><
选完一条边做割边后,要消除它对网络的影响,就要退还所有流过这条边的流量。
边(x,y)容量c,就
设源点为x,汇点为st,最大流
设源点为ed,汇点为y,最大流
用心感受一下,是不是很对呢?
我觉得这个最大流要加流量限制c,但题解的代码都没加,似乎是因为这个图的特殊性退还的流量一定为c?