网络流/最小割/最大权闭合图
2333好开心,除了一开始把$500^2$算成25000……导致数组没开够RE了一发,可以算是一次AC~
咳咳还是回归正题来说题解吧:
一拿到这道题,我就想:这是什么鬼玩意……矩阵乘法早忘了……画了半天也想不起来到底是谁乘谁,只记得有个式子:$c[i][j]=\sum a[i][k]*b[k][j]$
好吧没关系,既然画图不行了,我们就先拿这个东西,纯代数来搞!
D的表达式,里面那层我们可以写成:$\sum a[i][k]*b[k][j] - c[i][j]$
然而a和c都是1*N的矩阵,简化一下,我们得到:$$ \sum a[k]*b[k][j]-c[j]$$
所以我们把D的表达式转化一下,发现可以把c[j]提出来:$$ \begin{aligned} D&=\sum_{j=1}^n \big ( \sum_{k=1}^n a[k]*b[k][j]-c[j] \big )*a[j] \\ &=\sum_{j=1}^n \sum_{k=1}^n a[j]*a[k]*b[k][j]-\sum_{k=1}^n a[j]*c[j] \end{aligned} $$
这时我们发现:b[j][k]和c[j]前面分别乘了一或两个0/1变量……感觉像什么……最大权闭合图对不对……同时选了 j 和 k 的话可以得到b[j][k]的收益,但是选 j 得付出c[j]的代价!
这时候我们就可以轻松+愉快的进行建图……
好久没写过网络流了,一次写对Dinic还是蛮感动的
1 /************************************************************** 2 Problem: 3996 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:584 ms 7 Memory:29152 kb 8 ****************************************************************/ 9 10 //BZOJ 3996 11 #include<queue> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 typedef long long LL; 23 inline int getint(){ 24 int r=1,v=0; char ch=getchar(); 25 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 26 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 27 return r*v; 28 } 29 const int N=300010,M=1000010,INF=1e9; 30 /*******************template********************/ 31 int n,ans; 32 struct edge{int to,v;}; 33 struct Net{ 34 edge E[M<<1]; 35 int head[N],next[M<<1],cnt; 36 void ins(int x,int y,int z){ 37 E[++cnt]=(edge){y,z}; next[cnt]=head[x]; head[x]=cnt; 38 } 39 void add(int x,int y,int z){ins(x,y,z); ins(y,x,0);} 40 int d[N],S,T,cur[N]; 41 queue<int>Q; 42 bool mklevel(){ 43 memset(d,-1,sizeof d); 44 d[S]=0; 45 Q.push(S); 46 while(!Q.empty()){ 47 int x=Q.front(); Q.pop(); 48 for(int i=head[x];i;i=next[i]) 49 if (d[E[i].to]==-1 && E[i].v){ 50 d[E[i].to]=d[x]+1; 51 Q.push(E[i].to); 52 } 53 } 54 return d[T]!=-1; 55 } 56 int dfs(int x,int a){ 57 if (x==T) return a; 58 int flow=0; 59 for(int &i=cur[x];i && flow<a;i=next[i]) 60 if (d[E[i].to]==d[x]+1 && E[i].v){ 61 int f=dfs(E[i].to,min(E[i].v,a-flow)); 62 E[i].v-=f; 63 E[i^1].v+=f; 64 flow+=f; 65 } 66 if (!flow) d[x]=-1; 67 return flow; 68 } 69 void Dinic(){ 70 while(mklevel()){ 71 F(i,S,T) cur[i]=head[i]; 72 ans-=dfs(S,INF); 73 } 74 } 75 void init(){ 76 cnt=1; ans=0; 77 n=getint(); S=0; T=n*(n+1)+1; 78 int x; 79 F(i,1,n) F(j,1,n){ 80 x=getint(); 81 add(i*n+j,T,x); 82 add(i,i*n+j,INF); 83 add(j,i*n+j,INF); 84 ans+=x; 85 } 86 F(i,1,n){ 87 x=getint(); 88 add(S,i,x); 89 } 90 Dinic(); 91 printf("%d\n",ans); 92 } 93 }G1; 94 int main(){ 95 #ifndef ONLINE_JUDGE 96 freopen("3996.in","r",stdin); 97 freopen("3996.out","w",stdout); 98 #endif 99 G1.init(); 100 return 0; 101 }
3996: [TJOI2015]线性代数
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 590 Solved: 414
[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