【最大权闭合子图/最小割】BZOJ3438-小M的作物【待填】
【题目大意】
小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1...n编号),现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以获得c2i的额外收益,所以,小M很快地算出了种植的最大收益。
【思路】
首先,如果没有组合方案应该怎么做呢?其实非常方便。
首先建立超级源点S和超级汇点T,S向每个i连一条容量为ai的边,T向每个i连一条容量为bi的边。显然答案=总的容量之和-最小割。
那么如果有了组合方案呢?
对于每一个方案,我们可以拆为两个点u和v,由S向u连一条容量为c1i的边;由v向T连一条容量为c2i的边。然后由S向组合里的每一个点连一条容量为INF的边,由组合里的每一个点向T连一条容量为INF的边。显然割边必定会是和与S或者T相连接的点。
可能这里会有一个疑惑:如果一个集合中的点的割边在和S相连的点中,而u,v的割边在和T相连的点中(也就是说我们把农作物种在了A中,却算了收益在B中。)
事实上是——不可能的。我们可以证明:u、v和它们组合中的割边一定同时和S或者T连,否则必定有一条S到T的通路,就无法形成割了!
所以最后答案=总的容量之和-最小割
画画图就明白了:)
...T了,不知道为什么。
删掉了STL部分。还是T了。我要静静。
/*还是TLE,回头看*/ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define S 0 #define T MAXN-1 using namespace std; const int INF=0x7fffffff; const int MAXN=3000+50; const int MAXM=4004000+50; struct node { int fr,to,pos,cap; }; int n,m,sum; int dis[MAXN]; int vis[MAXN]; int cur[MAXN]; int first[MAXN],next[MAXM]; node edge[MAXM]; int tot=0; int read() { int x = 0; char ch = getchar(); while (ch < '0' || '9' < ch) ch = getchar(); while ('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x; } void addedge(int u,int v,int c) { edge[++tot]=(node){u,v,tot+1,c}; next[tot]=first[u];first[u]=tot; edge[++tot]=(node){u,v,tot-1,0}; next[tot]=first[v];first[v]=tot; } void build() { memset(first,-1,sizeof(first)); memset(next,-1,sizeof(next)); int ai,bi; n=read(); for (int i=1;i<=n;i++) { ai=read(); sum+=ai; addedge(S,i,ai); } for (int i=1;i<=n;i++) { bi=read(); sum+=bi; addedge(i,T,bi); } m=read(); int k,c1i,c2i; for (int i=1;i<=m;i++) { k=read();c1i=read();c2i=read(); int u=n+i; int v=n+i+m; addedge(S,u,c1i); addedge(v,T,c2i); sum+=c1i+c2i; for (int j=1;j<=k;j++) { int ci; ci=read(); addedge(u,ci,INF); addedge(ci,v,INF); } } } int bfs() { memset(dis,-1,sizeof(dis)); int l=0,r=0; int que[MAXN]; que[++r]=S; dis[S]=0; while (l<r) { int head=que[l];l++; for (int i=first[head];i!=-1;i=next[i]) { node &tmp=edge[i]; if (dis[tmp.to]==-1 && tmp.cap>0) { dis[tmp.to]=dis[head]+1; que[++r]=tmp.to; } } } return (dis[T]!=-1); } int dfs(int s,int t,int f) { vis[s]=1; if (s==t || !f) return f; int res=0; for (int i=first[s];i!=-1;i=next[i]) { node &tmp=edge[i]; if (!vis[tmp.to] && tmp.cap>0 && dis[tmp.to]==dis[s]+1) { int delta=dfs(tmp.to,t,min(tmp.cap,f)); if (delta>0) { tmp.cap-=delta; edge[tmp.pos].cap+=delta; f-=delta; res+=delta; if (!f) return res; } } } return res; } int dinic() { int flow=0; while (bfs()) { memset(vis,0,sizeof(vis)); flow+=dfs(S,T,INF); } return flow; } int main() { build(); printf("%d\n",sum-dinic()); return 0; }