【BZOJ3996】线性代数(TJOI2015)-最小割

测试地址:线性代数

做法:从题目看出题目求的是将A矩阵中元素分为两个集合(取0或取1)使某一个值最大,那么我们可以把这个值转化成某个常数减去某个变量,转成求该变量的最小值,我们注意关键词“分为两个集合”“最小值”,就开始思考这是不是能用最小割解决,实际上是可以的,转化方法见这里。然后写一个最大流算法求出最小割就可以了。

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define inf 1000000000
using namespace std;
int n,first[510]={0},tot=1,start=0,end;
int ans=0,sum=0,level[510],s[510]={0};
struct edge {int v,f,next;} e[1000010];

void insert(int a,int b,int f)
{
  e[++tot].v=b,e[tot].f=f,e[tot].next=first[a],first[a]=tot;
}

bool makelevel()
{
  queue <int> q;
  memset(level,-1,sizeof(level));
  level[start]=0;
  q.push(start);
  while(!q.empty())
  {
    int v=q.front();q.pop();
	if (v==end) return 1;
	for(int i=first[v];i;i=e[i].next)
	  if (level[e[i].v]==-1&&e[i].f)
	  {
	    level[e[i].v]=level[v]+1;
		q.push(e[i].v);
	  }
  }
  return 0;
}

int dfs(int v,int maxf)
{
  int ret=0,f;
  if (v==end) return maxf;
  for(int i=first[v];i;i=e[i].next)
    if (e[i].f&&level[e[i].v]==level[v]+1)
	{
	  f=dfs(e[i].v,min(maxf-ret,e[i].f));
	  ret+=f;
	  e[i].f-=f;
	  e[i^1].f+=f;
	  if (ret==maxf) return ret;
	}
  return ret;
}

int main()
{
  scanf("%d",&n);
  end=n+1;
  for(int i=1;i<=n;i++)
    for(int j=1,a;j<=n;j++)
	{
	  scanf("%d",&a);
	  sum+=a;s[i]+=a;
	  if (i!=j) insert(i,j,a),insert(j,i,0);
	}
  for(int i=1,c;i<=n;i++)
  {
    scanf("%d",&c);
	insert(i,end,c),insert(end,i,0);
    insert(start,i,s[i]),insert(i,start,0);
  }
  
  while(makelevel())
  {
    ans+=dfs(start,inf);
  }
  printf("%d",sum-ans);
  
  return 0;
}


原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793793.html