「TJOI2015」线性代数

题目链接

Solution:

       AB[i]=\sum_{j=1}^{n}A[j]*B[j][i]  

       (AB-C)[i]=\sum_{j=1}^{n}A[j]*B[j][i]-C[j]

       D=(AB-C)A^{T}=\sum_{i=1}^{n}A[i]\sum_{j=1}^{n}A[j]*B[j][i]-C[j]

            =\sum_{i=1}^{n}\sum_{j=1}^{n}A[i]*A[j]*B[i][j]-\sum_{i=1}^{n}A[i]*C[i]

       抽象一下就是:同时选ij可以获得B[i][j]的价值,而选了i就要失去C[i]的价值,求最大获益。

       考虑网络流,源点向每一个(i,j)连一条流量为B[i][j]的边,接着(i,j)向每一个ij分别连一条流量为无限的边。最后每一个i再向汇点连一条流量为C[i]的边。

       我们考虑这样一张流量图的割意味着什么,对于每一个i,如果你想要断去经过它到汇点的路径,要么割掉左边所有相连的(i,j),要么割掉右边流量为C[i]的边.

       记F=\sum_{i=1}^{n}\sum_{j=1}^{n}B[i][j],则F-割的大小就是一组可行的解。所以F-最小割就是最大获益,即求F-最大流。

 

Code:

#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
template<typename T> void read(T &num){
	char c=getchar();T f=1;num=0;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
	num*=f;
}
template<typename T> void qwq(T x){
	if(x>9)qwq(x/10);
	putchar(x%10+'0');
}
template<typename T> void write(T x){
	if(x<0){x=-x;putchar('-');}
	qwq(x);putchar('\n');
}
int B[510][510];int C[510]; 
struct wzy{
	int nxt,vertice,w;
}edge[1600010];
int head[260010];int len=1;
inline void add_edge(int x,int y,int z){
	edge[++len].nxt=head[x];edge[len].vertice=y;edge[len].w=z;head[x]=len;
	edge[++len].nxt=head[y];edge[len].vertice=x;edge[len].w=0;head[y]=len;return;
}

int s=0;int t=0;
queue<int>v;int d[260010];
inline bool bfs(){
	memset(d,0,sizeof(d));
	while(!v.empty())v.pop();
	d[s]=1;v.push(s);
	while(!v.empty()){
		int x=v.front();v.pop();
		for(int i=head[x];i;i=edge[i].nxt){
			int nop=edge[i].vertice;
			if(d[nop]||!edge[i].w)continue;
			d[nop]=d[x]+1;v.push(nop);
			if(nop==t)return true;
		} 
	}
	return false;
}
inline int dinic(int x,int val){
	if(x==t)return val;
	int k=0;
	for(int i=head[x];i&&k!=val;i=edge[i].nxt){
		int nop=edge[i].vertice;
		if(!edge[i].w||d[nop]!=d[x]+1)continue;
		int temp=dinic(nop,min(val-k,edge[i].w));
		if(!temp)d[nop]=0;
		edge[i].w-=temp;edge[(i^1)].w+=temp;k+=temp;
	}
	return k;
}

int main(){
	int n;read(n);int ans=0;
	rep(i,1,n){
		rep(j,1,n)read(B[i][j]);
	}
	rep(i,1,n)read(C[i]); 
	
	s=0;t=n*n+n+1;
	rep(i,1,n){
		rep(j,1,n){
			add_edge(s,(i-1)*n+j,B[i][j]);ans+=B[i][j];
			add_edge((i-1)*n+j,n*n+i,INT_MAX); 
			add_edge((i-1)*n+j,n*n+j,INT_MAX);
		}
	} 
	rep(i,1,n)add_edge(n*n+i,t,C[i]);
	
	int max_flow=0;int flow=0;
	while(bfs()){
		do{
			flow=dinic(s,INT_MAX);max_flow+=flow;
		}while(flow);
	}
	write(ans-max_flow);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值