[bzoj3996][网络流-最小割]线性代数

35 篇文章 0 订阅

Description

给出一个NN的矩阵B和一个1N的矩阵C。求出一个1*N的01矩阵A.使得

D=(A*B-C)*AT最大。其中AT为A的转置。输出D

Input

第一行输入一个整数N,接下来N行输入B矩阵,第i行第J个数字代表Bij.
接下来一行输入N个整数,代表矩阵C。矩阵B和矩阵C中每个数字都是不超过1000的非负整数。

Output

输出最大的D

Sample Input

3

1 2 1

3 1 0

1 2 3

2 3 7

Sample Output

2

HINT

1<=N<=500

题解

我怎么连这种题都秒不了啊好垃圾…
式子出来大概是这样
a [ i ] ∗ a [ j ] ∗ B [ i ] [ j ] − a [ i ] ∗ C [ i ] a[i]*a[j]*B[i][j]-a[i]*C[i] a[i]a[j]B[i][j]a[i]C[i]
是一个模型
n 2 n^2 n2个物品 每个物品有价值 选第(i,j)个物品要花费C[i]+C[j]的钱 求最大价值
st->每个B[i][j]连边 边权B[i][j]表示这个物品如果用了有B[i][j]的价值
每个B[i][j]->i和j连边 边权INF 表示这条边不能割掉
每个i->ed连边 边权C[i] 表示如果叉掉这条边的话就可以对B[i][j]做贡献
最小割即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define zero 5000005
#define inf (1LL<<63-1)
using namespace std;
struct node{int x,y,next;LL c;}a[5110000];int len,last[505*505+5];
void ins(int x,int y,LL c)
{
	len++;
	a[len].x=x;a[len].y=y;a[len].c=c;
	a[len].next=last[x];last[x]=len;
	len++;
	a[len].x=y;a[len].y=x;a[len].c=0;
	a[len].next=last[y];last[y]=len;
}
queue<int> li;
int st,ed;
int h[505*505+5];
bool bt_h()
{
	memset(h,0,sizeof(h));h[st]=1;
	li.push(st);
	while(!li.empty())
	{
		int x=li.front();
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(!h[y]&&a[k].c)h[y]=h[x]+1,li.push(y);
		}
		li.pop();
	}
	return h[ed]>0;
}
LL findflow(int x,LL f)
{
	if(x==ed)return f;
	LL s=0,t;
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(h[y]==h[x]+1&&a[k].c&&s<f)
		{
			s+=(t=findflow(y,min(a[k].c,f-s)));
			a[k].c-=t;a[k^1].c+=t;
		}
	}
	if(!s)h[x]=0;
	return s;
}
int n,B[505][505],C[505];
int pt(int x,int y){return x*n+y;}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&B[i][j]);
	for(int i=1;i<=n;i++)scanf("%d",&C[i]);
	LL sum=0;len=1;
	st=n*(n+1)+1;ed=st+1;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
	{
		ins(st,pt(i,j),B[i][j]);sum+=B[i][j];
		ins(pt(i,j),i,inf);ins(pt(i,j),j,inf);
	}
	for(int i=1;i<=n;i++)ins(i,ed,C[i]);
	while(bt_h())sum-=findflow(st,inf);
	printf("%lld\n",sum);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值