Link
先把题意抽象一下:给定一个存在一部分为定向的边的竞赛图,最大化它的三元环个数。
我们知道竞赛图的三元环个数为({nchoose 3}-sumlimits_{i=1}^n{deg_ichoose 2})。
对于一条未定向的边((u,v)),它会使(u,v)其中一个点的度数加一。
对于一个点而言,随着度数的增加,度数加一使得三元环减少的个数也是在增加的,也就是说这是一个凸函数。
那么直接费用流就行了,建图就是凸函数差分的那一套方法。
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
const int N=107,V=6007,E=50007,inf=1e9;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int s,t,tot=1,a[N][N],deg[N],head[V],ver[E],next[E],edge[E],cost[E],dis[V],flow[V],inq[V],id[V];std::queue<int>q;struct node{int u,v,id;}p[N*N];
void add(int u,int v,int f,int c)
{
ver[++tot]=v,next[tot]=head[u],head[u]=tot,edge[tot]=f,cost[tot]=c;
ver[++tot]=u,next[tot]=head[v],head[v]=tot,edge[tot]=0,cost[tot]=-c;
}
int spfa()
{
memset(dis+1,0x3f,t<<2),q.push(s),inq[s]=1,flow[s]=inf,dis[s]=0,id[t]=-1;
for(int i,u,v;!q.empty();)
for(i=head[u=q.front()],q.pop(),inq[u]=0;i;i=next[i])
if(edge[i]&&dis[v=ver[i]]>dis[u]+cost[i])
if(dis[v]=dis[u]+cost[i],id[v]=i,flow[v]=std::min(flow[u],edge[i]),!inq[v])
q.push(v),inq[v]=1;
return ~id[t];
}
int EK()
{
int mincost=0;
for(int p;spfa();) for(mincost+=flow[t]*dis[t],p=t;p^s;p=ver[id[p]^1]) edge[id[p]]-=flow[t],edge[id[p]^1]+=flow[t];
return mincost;
}
int main()
{
int n=read(),ans=n*(n-1)*(n-2)/6,cnt=n;s=n*(n+1)/2+1,t=s+1;
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=read();
for(int i=1;i<n;++i) for(int j=i+1;j<=n;++j) if(!a[i][j]) ++deg[j]; else if(a[i][j]==1) ++deg[i]; else add(s,++cnt,1,0),add(cnt,j,1,0),add(cnt,i,1,0),p[cnt]={i,j,tot};
for(int i=1;i<=n;++i) for(int j=deg[i];j<n;++j) add(i,t,1,j);
for(int i=1;i<=n;++i) ans-=deg[i]*(deg[i]-1)/2;
printf("%d
",ans-EK());
for(int i=n+1;i<=cnt;++i) a[p[i].v][p[i].u]=!(a[p[i].u][p[i].v]=edge[p[i].id]);
for(int i=1;i<=n;++i,puts("")) for(int j=1;j<=n;++j) printf("%d ",a[i][j]);
}