=== ===
這裏放傳送門
=== ===
題解
這道題如果考慮直接計算合法的三元組數目好像有點不大科學。。因爲只有三場都比完了才能發現這是一個合法組。那麼如果考慮不合法情況呢?合法情況肯定是一個有向的三元環,那麼不合法情況就是從一個點出發了兩條邊。也就是說如果有一個人贏了兩場比賽,那麼就會產生一個不合法三元組。以此類推,如果 有一個人P一共贏了k個人,那麼從這k個人裏面隨便選出兩個人來跟P構成的三元組肯定都是不合法的。
那麼只需要求出能夠產生最少不合法組的情況就可以了。設
一開始感覺邊好像會很多然後寫了個動態加邊。。然而好像不用動態加邊也是可以的。對於構造方案的話,可以發現每一場比賽連出去的那兩條邊的流量情況就代表了比賽的輸贏,可以直接訪問這些邊得出合法方案。
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1000000000
#define inc(x)(x=(x==11000)?1:x+1)
using namespace std;
int n,a[110][110],p[11010],dis[11010],q[11010],head,tail,pre[11010],tot,S,T,Cost;
int sum[110],cnt,num[110][110],rec[110][110];
bool ext[11010];
struct edge{
int to,flw,cst,nxt,id;
}e[500010];
void add(int from,int to,int flow,int cost,int idf){
e[tot].to=to;e[tot].flw=flow;e[tot].cst=cost;
e[tot].nxt=p[from];e[tot].id=idf;p[from]=tot++;
}
int calc(int v){return v*(v-1)/2;}
void change(int now){
int v=e[now].id,val;
e[now].id=e[now^1].id=0;
++sum[v];val=calc(sum[v]+1)-calc(sum[v]);
add(v+cnt,T,1,val,v);//動態加邊
add(T,v+cnt,0,-val,-v);
}
int Increase(int S,int T){
int Min=inf;
for (int i=T;i!=S;i=e[pre[i]^1].to)
Min=min(Min,e[pre[i]].flw);
for (int i=T;i!=S;i=e[pre[i]^1].to){
e[pre[i]].flw-=Min;
e[pre[i]^1].flw+=Min;
if (e[pre[i]].id>0)
change(pre[i]);
}
return Min;
}
bool SPFA(int &Cost){
int delta;
memset(dis,127,sizeof(dis));
head=0;tail=1;q[tail]=S;
ext[S]=true;dis[S]=0;
while (head!=tail){
int u;
inc(head);u=q[head];ext[u]=false;
for (int i=p[u];i!=-1;i=e[i].nxt)
if (e[i].flw>0&&dis[e[i].to]>dis[u]+e[i].cst){
int v=e[i].to;pre[v]=i;
dis[v]=dis[u]+e[i].cst;
if (ext[v]==false){
inc(tail);q[tail]=v;ext[v]=true;
}
}
}
if (dis[T]>inf) return false;
delta=Increase(S,T);
Cost+=delta*dis[T];
return true;
}
int main()
{
memset(p,-1,sizeof(p));
scanf("%d",&n);S=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
if (a[i][j]==1) ++sum[i];
if (a[i][j]==2){
if (i<=j){
num[i][j]=++cnt;
add(S,cnt,1,0,0);
add(cnt,S,0,0,0);
}
}
}
T=cnt+n+1;
for (int i=1;i<=n;i++){
int val=calc(sum[i]);
Cost+=val;//記錄已經存在的不合法三元組
val=calc(sum[i]+1)-val;//計算增量
add(i+cnt,T,1,val,i);
add(T,i+cnt,0,-val,-i);
}
memset(rec,-1,sizeof(rec));
for (int i=1;i<=n;i++)
for (int j=i;j<=n;j++)
if (a[i][j]==2){
int now=num[i][j];
rec[i][j]=tot;//記錄每一場比賽對應的邊的編號
add(now,i+cnt,1,0,0);add(i+cnt,now,0,0,0);
add(now,j+cnt,1,0,0);add(j+cnt,now,0,0,0);
}
while (SPFA(Cost));
Cost=n*(n-1)*(n-2)/6-Cost;
printf("%d\n",Cost);
for (int i=1;i<=n;i++)
for (int j=i;j<=n;j++){
int v=rec[i][j];
if (v==-1) continue;
if (e[v].flw==0){a[i][j]=1;a[j][i]=0;}
else{a[i][j]=0;a[j][i]=1;}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
printf("%d%c",a[i][j]," \n"[j==n]);
return 0;
}