3996: [TJOI2015]线性代数
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 1499 Solved: 885
[ Submit ][ Status ][ Discuss ]
Description
给出一个N*N的矩阵B和一个1*N的矩阵C。求出一个1*N的01矩阵A.使得
D=(A*B-C)*A^T最大。其中A^T为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
看完题之后,推了好久式子,然而,只是在浪费时间 括弧泪
找题解
Wa!!怎么是网络流!!最大权闭合子图 ,我果然还是太蒟蒻了 括弧泪
那么既然学会了,就应该写一篇高质量的博客
我们发现:
如果想取B[i][j]那么一定要保证a[i]、a[j]都为1,a[i]、a[j]都为1,则c[i],c[j]就都要取
那么把它们抽象成点,对于一个点,向必须与它同时选的点连边
并且点具有点权(此题中,B中的点权为正1,C中的为负)
所以它的最大权闭合子图就是要求的解
现在就来说一些关于最大全闭合子图的东西
材料来自:传送门
现在有一个有向图,每个点有点权,点权可正可负。
对于任意一条有向边i和j,选择了点i就必须选择点j,
你需要选择一些点使得得到权值最大。
这个问题可以用网络流解决。
建图方法:对于任意点i,如果i权值为正,s向i连容量为其权值的边,
否则i向t连容量为其权值的绝对值的边。
原图所有边容量为正无穷。则最大权=正权和-最大流。
如何证明呢?我们把最大流理解成最小割,那么割掉的边一定不可能是正无穷的边。
我们发现,选择一个正权点即不割掉s到其的边,选择一个负权点即割掉其到t的边。
现在证明方案合法:
对于依赖关系i到j:
假设i点权为正j点权为负。
选了i不选j即没有割掉s到i的边而且没有割掉j到t的边,显然s和t联通,不符合最小割定义。
假设i点权为负j点权为正。
选了i不选j即割掉i到t的边而且割掉s到j的边,由于s到t现在不连通,我们不割
这两条边同样s和t是不联通的,那么割这两边不满足割量最小,不符合最小割定义。
其余情况同理,不符合割量最小。
注意这个算法不需要原图是DAG。
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<queue>
#include<set>
#include<map>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f*x;
}
const int N=3000000;const int inf=0X3f3f3f3f;
int n,m,ecnt=1,last[N],cur[N],q[N],d[N],S,T=N-10,ans;
struct EDGE{int to,nt,val;}e[N];
inline void readd(int u,int v,int val)
{e[++ecnt]=(EDGE){v,last[u],val};last[u]=ecnt;}
inline void add(int u,int v,int val)
{readd(u,v,val);readd(v,u,0);}
bool bfs()
{
memset(d,0,sizeof(d));
d[S]=1;int head=0,tail=1;q[0]=S;
while(head<tail)
{
int u=q[head++];
for(int i=last[u];i;i=e[i].nt)
if(e[i].val&&!d[e[i].to])
{
d[e[i].to]=d[u]+1;
q[tail++]=e[i].to;
}
}
return d[T];
}
int dfs(int u,int limit)
{
if(u==T||!limit)return limit;
int flow=0;
for(int i=cur[u];i;i=e[i].nt)
if(e[i].val&&d[u]+1==d[e[i].to])
{
int tmp=dfs(e[i].to,min(e[i].val,limit));
e[i].val-=tmp;e[i^1].val+=tmp;flow+=tmp;limit-=tmp;
if(e[i].val)cur[u]=i;if(!limit)break;
}
if(!flow)d[u]=-1;
return flow;
}
inline void dinic()
{while(bfs()){for(int i=0;i<=n*n+n;i++)cur[i]=last[i];cur[T]=last[T];ans-=dfs(S,inf);}}
int main()
{
n=read();int tmp=n,temp;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
{
temp=read();ans+=temp;
add(S,++tmp,temp);
add(tmp,i,inf);add(tmp,j,inf);
}
for(int i=1;i<=n;i++)
{temp=read();add(i,T,temp);}
dinic();
printf("%d\n",ans);
return 0;
}