給一張競賽圖,圖中還有一些邊沒有定向,現在要把它們重新定向,要求重定向之後三元環盡量多。
直接考慮不可取,逆向思維。算出極限情況可能出現三元環的個數,那麼就是n×(
n−1)
×(n−
2)
6\dfrac
6n×(n−
1)×(
n−2)
,接下來考慮什麼情況會拆毀三元環。
考慮乙個點的負場情況來判斷少了多少三元環。
如果乙個點負場為2
22或更多,那麼只要從能打過它的點裡面隨便挑出兩個點就不是乙個三元環。
按照本類題目的一般做法來做的話,把那些沒定向的邊看為點,稱之為邊對應的點,那麼就可以這樣建圖:
那麼之後邊對應的點流向那個點就表明設定的是那個人輸了。
那麼接下來就要考慮人如何連到匯點了。直接做一條邊顯然是不可行的。
考慮乙個點從x
xx負場到x+1
x+1x+
1負場會破壞多少三元環。
那麼就是x×(
x+1)
2−x×
(x−1
)2=x
\dfrac-\dfrac=x
2x×(x+
1)−
2x×(
x−1)
=x。
所以我們可以考慮拆邊。
讓乙個人往匯點連上n
nn條邊,第i
ii條邊流量為1
11,費用為iii。
這樣就可以了。
c od
e:
code:
code
:
#include
#define regi register int
int n;
int s,t;
int ans;
int mincost;
int vis[
100100];
int win[
100100];
int dis[
100100];
std::queue<
int>q;
int a[
1000][
1000];
int be[
10100][
110]
;int head[
100100
],tot=1;
struct edgee[
100000];
void
add(
int x,
int y,
int flow,
int cost)
; head[x]
=tot;
e[++tot]=;
head[y]
=tot;
}bool
spfa()
q.push
(s);
dis[s]=0
;while
(!q.
empty()
)}}}
return dis[t]
!=0x3f3f3f3f;}
intdfs
(int x,
int min)
vis[x]=1
;int flow=0;
for(regi i=head[x]
,w;i;i=e[i]
.nxt)
}return flow;
}void
dinic()
}}main()
}//先算出現在少了多少三元環
for(regi i=
1;i<=n;
++i)
//拆邊,按照等差數列建邊。
dinic()
;//跑圖。
printf
("%d\n"
,ans-mincost)
;for
(regi i=
1;i<=n;
++i)
for(regi j=i+
1;j<=n;
++j)
}//檢索哪條邊流滿了。
for(regi i=
1;i<=n;
++i,
puts(""
))for(regi j=
1;j<=n;
++j)
printf
("%d "
,a[i]
[j])
;return0;
}
Wc2007 剪刀石頭布
題目描述 bzluogu 題解 最小費用流。對於三支隊伍,勝負情況只有 2 種。一種是形成三元環,另一種是 x 贏兩場,y 贏一場,z 沒贏過。所以我們統計一下另一種最少有多少種就好了。最後答案就是 c 3 n k 對於乙個隊伍 x 若其勝場數為 w x 則會造成的負貢獻為 c 2 不會搞?作差啊。...
WC2007 剪刀石頭布
正著求不太好求,但是不是剪刀石頭布的又很好表示 三個人中恰好有乙個人贏了兩場。所以我們考慮讓這種三元組數量最少使得剪刀石頭布最多。考慮乙個人如果贏了i場,那麼他對 非剪刀石頭布的三元組的貢獻是 i i 1 2 也就是他和任意兩個被他擊敗的人都可以組成三元組。並且每個人的貢獻都是獨立的,不會有重複 因...
Wc2007 剪刀石頭布(費用流)
time limit 20 sec memory limit 128 mb sec special judge 在一些一對一遊戲的比賽 如下棋 桌球和羽毛球的單打 中,我們經常會遇到a勝過b,b勝過c而c又勝過a的有趣情況,不妨形象的稱之為剪刀石頭布情況。有的時候,無聊的人們會津津樂道於統計有多少這...