题目大意
给出一个图,图中有些边的方向没有确定,有些方向给定,要求你定下不确定边的方向,使得图中的三元环最多。
问题求解
对于三元环的个数,我们直接求解非常困难,所以考虑补集,用总的个数减去不能构成三元环的个数,也就是总的三元环的个数了。
显然,任意三个点构成三元环的个数是 (C_n^3)。
再来思考不能构成三元环的个数,我们可以发现,如果一个点不能构成三元环,那么这个点的出度为 (2),那么如果一个点的出度为 (du[x]) 的话,不能构成三元环的个数就是 (C_{du[x]}^2=frac{du[x] imes (du[x]-1)}{2})。
所以我们要求的答案就变成了 (C_n^3-sum C_{du[x]}^2)。
因此,要最小化 (sum C_{du[x]}^2) 的值。
如果一条((u,v))边,假设确定的边我们只能将 (du[v]+1),如果不确实可以将 (du[u]+1) 或 (du[v]+1) ,(u) 和 (v) 二选一,又注意到边数比较小,所以考虑网络流,将边看成一个"点",从边流向点再流向超级汇点。
如果一条"边"使得 (du[u]+1) 那么建立一条从这个"边"到 (u) 点的边,流量为 (1),费用为 (0) 如果一条边是没有确定的,那么建立一条到 (u) 点的边和一条到 (v) 的边,流量为 (1),费用为 (0) ,代表二选一,从超级源点到每条"边"也建立一条流量为 (1) ,费用为 (0) 的边,限制流量。
考虑如何计算答案,观察柿(式)子 (C_{du[x]}^2=frac{du[x] imes (du[x]-1)}{2}) ,发现后面那个柿子很想小学的时候学过的等差数列 (0+1+2+3+...+du[x-1]=frac{du[x] imes (du[x]-1)}{2}) ,那么我们建(du[x]) 条边,从点到超级汇点,流量为 (1),费用分别为 (0,1,2,...,du[x]-1),就可以表示所有的情况了。
然后就一般的刷费用流就好了。
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=10105,maxe=maxn*8,INF=0x3f3f3f3f;
int N,mp[105][105],st,ed;
int lnk[maxn],nxt[maxe],son[maxe],w[maxe],cost[maxe],cnt=1,vis[maxn],dis[maxn],Q[maxn];
int maxflow,mincost,Ans;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
inline int calc(int x,int y){return (x-1)*N+y;}
inline void add_e(int x,int y,int z,int c){
son[++cnt]=y;nxt[cnt]=lnk[x];lnk[x]=cnt;w[cnt]=z;cost[cnt]=c;
}
inline void add_edge(int x,int y,int z,int c){
add_e(x,y,z,c);add_e(y,x,0,-c);
}
int spfa(){
int hed=0,til=1;
memset(dis,INF,sizeof dis);
memset(vis,0,sizeof vis);
dis[st]=0;Q[til]=st;
while(hed!=til){
hed=(hed+1)%maxn;vis[Q[hed]]=0;
for(int j=lnk[Q[hed]];j;j=nxt[j])
if(w[j]>0&&dis[son[j]]>dis[Q[hed]]+cost[j]){
dis[son[j]]=dis[Q[hed]]+cost[j];
if(!vis[son[j]]){
vis[son[j]]=1;Q[til=(til+1)%maxn]=son[j];
}
}
}
return dis[ed]!=INF;
}
int min(int x,int y){return x<y?x:y;}
int DFS(int x,int delta){
vis[x]=1;
if(x==ed)return delta;
int flow=0;
for(int j=lnk[x];j;j=nxt[j]){
if((!vis[son[j]]||x==ed)&&w[j]>0&&dis[son[j]]==dis[x]+cost[j]){
int t=DFS(son[j],min(delta-flow,w[j]));
if(t>0){flow+=t;w[j]-=t;w[j^1]+=t;mincost+=t*cost[j];}
// if(flow==delta)break;
}
}
return flow;
}
inline void Dinic(){
while(spfa()){
vis[ed]=1;
while(vis[ed]){
memset(vis,0,sizeof vis);
int tmp=DFS(st,INF);
maxflow+=tmp;
}
}
Ans=((N-1)*(N-2)*N)/6;
Ans-=mincost;
}
int main(){
N=read();st=0;ed=N*N+N+1;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++){
mp[i][j]=read();
if(i>=j)continue;
if(!(mp[i][j]^2)){
add_edge(st,calc(i,j),1,0);
add_edge(calc(i,j),N*N+i,1,0);
add_edge(calc(i,j),N*N+j,1,0);
}
else if(!(mp[i][j]^1)) add_edge(st,N*N+j,1,0);
else add_edge(st,N*N+i,1,0);
}
for(int i=1;i<=N;i++)
for(int j=0;j<N;j++)
add_edge(N*N+i,ed,1,j);
Dinic();
printf("%d
",Ans);
for(int i=1;i<=N;i++){
for(int j=i+1;j<=N;j++){
if(mp[i][j]<2)continue;
for(int k=lnk[calc(i,j)];k;k=nxt[k]){
if(son[k]&&w[k]==0){
mp[i][j]=(son[k]-N*N==j);
mp[j][i]=mp[i][j]^1;
}
}
}
}
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++)printf("%d ",mp[i][j]);
printf("
");
}
return 0;
}