3894:矩形,每个点选文科一个得分,理科一个得分,一个点上下左右都和这个点选的一样会有额外得分。求最大得分。
最小割。把所有权值加上,s连每个点,每个点连t,流量均为得分。全选s的特殊得分建一个点,s连它,流量为得分,它连所有包含的点,流量无穷(意为这个关系不可割断)。全选t的同理。
这样所有矛盾关系都对应一条从s到t的通路,最小割即可。
注意dinic增广过程中把一个点设成-1的条件是分配到这个点的流量有剩余,那么这个点肯定流不了了。否则,可能这个点还有增流的能力。
#include<cstdio>
#define gm 101
using namespace std;
struct edge
{
int to,flow;
}table[300000];
int head[50000],nex[300000],tot=1;
inline void add(int x,int y,int f)
{
++tot;
table[tot].to=y;
table[tot].flow=f;
nex[tot]=head[x];
head[x]=tot;
}
inline void link(int x,int y,int f)
{
add(x,y,f);
add(y,x,0);
}
const int inf=0x7fffffff;
int s=0,t;
int n,m,v,top;
int pos[gm][gm],arts[gm][gm],sciences[gm][gm];
int x[]={1,-1,0,0,0},y[]={0,0,1,-1,0};
int ans=0;
int d[50000];
#include<queue>
#include<cstring>
#include<algorithm>
inline bool bfs()
{
static queue<int> q;
memset(d,-1,sizeof d);
d[s]=0;
q.push(s);
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=head[now];i;i=nex[i])
{
edge& e=table[i];
if(e.flow&&d[e.to]==-1)
d[e.to]=d[now]+1,q.push(e.to);
}
}
return d[t]!=-1;
}
int send(int p,int flow)
{
if(p==t) return flow;
int last=flow,kre;
for(int i=head[p];i;i=nex[i])
{
int to=table[i].to;
if(!table[i].flow||d[to]!=d[p]+1) continue;
last-=kre=send(to,min(last,table[i].flow));
if(!kre) d[to]=-1;
table[i].flow-=kre;
table[i^1].flow+=kre;
if(!last) break;
}
if(last) d[p]=-1;
return flow-last;
}
inline int dinic()
{
int sum=0;
while(bfs())
sum+=send(s,inf);
return sum;
}
int main()
{
scanf("%d%d",&n,&m);
t=n*m*3+1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
pos[i][j]=++top;
scanf("%d",&v);
ans+=v;
link(s,pos[i][j],v);
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
scanf("%d",&v);
ans+=v;
link(pos[i][j],t,v);
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
arts[i][j]=++top;
scanf("%d",&v);
ans+=v;
link(s,arts[i][j],v);
for(int k=0;k<5;++k)
{
int xx=i+x[k],yy=j+y[k];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m)
link(arts[i][j],pos[xx][yy],inf);
}
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
sciences[i][j]=++top;
scanf("%d",&v);
ans+=v;
link(sciences[i][j],t,v);
for(int k=0;k<5;++k)
{
int xx=i+x[k],yy=j+y[k];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m)
link(pos[xx][yy],sciences[i][j],inf);
}
}
printf("%d\n",ans-dinic());
return 0;
}
1497:选用户有一个收入,选电站有一个花费,选某个用户就必须选指定的电站。
S->用户->电站->T,最小割即可。
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
int s=0,t;
const int inf=0x7fffffff;
struct e
{
int t,flow;
e *n,*r;
e(int t,int flow,e *n):t(t),flow(flow),n(n){}
}*f[55005];
inline void link(int a,int b,int flow)
{
e *x=f[a]=new e(b,flow,f[a]),*y=f[b]=new e(a,0,f[b]);
x->r=y,y->r=x;
}
int p,a,b,c;
int d[55005];
inline bool bfs()
{
memset(d+1,-1,n+m+1<<2);
static queue<int> q;
q.push(s);
while(!q.empty())
{
int now=q.front();q.pop();
for(e *i=f[now];i;i=i->n)
if(i->flow&&d[i->t]==-1)
d[i->t]=d[now]+1,q.push(i->t);
}
return d[t]!=-1;
}
int send(int x,int maxf)
{
if(x==t) return maxf;
int tot=0;
for(e *i=f[x];i;i=i->n)
if(i->flow&&d[i->t]==d[x]+1)
{
int kre=send(i->t,min(maxf,i->flow));
tot+=kre;
if(!kre) d[i->t]=-1;
i->flow-=kre;
i->r->flow+=kre;
if(!(maxf-=kre)) break;
}
return tot;
}
inline int dinic()
{
int ans=0;
while(bfs())
ans+=send(s,inf);
return ans;
}
int ans=0;
int main()
{
scanf("%d%d",&n,&m);
t=n+m+1;
for(int i=1;i<=n;++i)
{
scanf("%d",&p);
link(i,t,p);
}
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&a,&b,&c);
ans+=c;
link(s,i+n,c);
link(i+n,a,inf);
link(i+n,b,inf);
}
printf("%d\n",ans-dinic());
return 0;
}
1711:每一头牛可以吃一些种食物和一些饮料,现有一些食物和饮料,每种一个,问最多有多少牛可以得到能吃的饮料和食物各一种
s->饮料->牛->食物->t,要注意的是每头牛最多只能被满足一次,因此将每头牛拆成两个点,中间连一条流量1的边,就限制住了流量。
数组切记不要开小。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
struct edge
{
int to,flow,nex;
}table[10000];
int head[1000],top=1;
inline void add(int x,int y,int f)
{
table[++top].to=y;
table[top].flow=f;
table[top].nex=head[x];
head[x]=top;
}
inline void link(int x,int y,int f)
{
add(x,y,f);
add(y,x,0);
}
int n,f,d;
int s=0,t;
int a,b,v;
int dep[1000];
inline bool bfs()
{
static queue<int> q;
memset(dep,-1,sizeof dep);
dep[s]=0;
q.push(s);
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=head[now];i;i=table[i].nex)
{
int to=table[i].to;
if(table[i].flow&&dep[to]==-1)
dep[to]=dep[now]+1,q.push(to);
}
}
return dep[t]!=-1;
}
int send(int x,int flow)
{
if(x==t) return flow;
int last=flow,kre;
for(int i=head[x];i&&last;i=table[i].nex)
{
int to=table[i].to;
if(!table[i].flow||dep[to]!=dep[x]+1) continue;
last-=kre=send(to,min(last,table[i].flow));
table[i].flow-=kre;
table[i^1].flow+=kre;
}
if(last) dep[x]=-1;
return flow-last;
}
inline int dinic()
{
int sum=0;
while(bfs())
sum+=send(s,0x7fffffff);
return sum;
}
int main()
{
scanf("%d%d%d",&n,&f,&d);
t=f+d+n+n+1;
for(int i=1;i<=f;++i)
link(s,i,1);
for(int i=1;i<=d;++i)
link(f+i,t,1);
int now=f+d+1;
for(int i=1;i<=n;++i)
{
link(now,now+1,1);
scanf("%d%d",&a,&b);
while(a--)
{
scanf("%d",&v);
link(v,now,1);
}
while(b--)
{
scanf("%d",&v);
link(now+1,f+v,1);
}
now+=2;
}
printf("%d\n",dinic());
return 0;
}
3438:每种作物种到A一个收益,种到B一个收益,有一些组合,种到A一个收益,种到B一个收益,求最大收益。
和第一题一样。依旧注意数组不要开小。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
struct e
{
int t,f;
e *n,*r;
e(int t,int f,e *n):t(t),f(f),n(n){}
}*f[10000];
int n,m,v;
const int s=0,t=9999,inf=0x7fffffff;
inline void link(int x,int y,int flow)
{
f[x]=new e(y,flow,f[x]);
f[y]=new e(x,0,f[y]);
f[x]->r=f[y];
f[y]->r=f[x];
}
unsigned long long ans=0;
int d[10000];
inline bool bfs()
{
static queue<int> q;
memset(d,-1,sizeof d);
d[s]=0;
q.push(s);
while(!q.empty())
{
int now=q.front();q.pop();
for(e *i=f[now];i;i=i->n)
if(i->f&&d[i->t]==-1)
d[i->t]=d[now]+1,q.push(i->t);
}
return d[t]!=-1;
}
int send(int now,int flow)
{
if(now==t) return flow;
int last=flow;
for(e *i=f[now];i&&last;i=i->n)
if(i->f&&d[i->t]==d[now]+1)
{
int kre=send(i->t,min(last,i->f));
last-=kre;
i->f-=kre;
i->r->f+=kre;
}
if(last) d[now]=-1;
return flow-last;
}
unsigned long long dinic()
{
unsigned long long sum=0;
while(bfs())
sum+=send(s,inf);
return sum;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&v);
link(s,i,v);
ans+=v;
}
for(int i=1;i<=n;++i)
{
scanf("%d",&v);
link(i,t,v);
ans+=v;
}
scanf("%d",&m);
int tot=n+1;
for(int i=1;i<=m;++i,tot+=2)
{
int k;scanf("%d",&k);
scanf("%d",&v),link(s,tot,v),ans+=v;
scanf("%d",&v),link(tot+1,t,v),ans+=v;
while(k--)
scanf("%d",&v),link(tot,v,inf),link(v,tot+1,inf);
}
printf("%llu\n",ans-dinic());
return 0;
}