*【HDU - 2819】Swap(二分图匹配,输出路径)(待证明:是否是最少交换次数?)
題干:
Given an N*N matrix with each entry equal to 0 or 1. You can swap any two rows or any two columns. Can you find a way to make all the diagonal entries equal to 1?
Input
There are several test cases in the input. The first line of each test case is an integer N (1 <= N <= 100). Then N lines follow, each contains N numbers (0 or 1), separating by space, indicating the N*N matrix.
Output
For each test case, the first line contain the number of swaps M. Then M lines follow, whose format is “R a b” or “C a b”, indicating swapping the row a and row b, or swapping the column a and column b. (1 <= a, b <= N). Any correct answer will be accepted, but M should be more than 1000.?
If it is impossible to make all the diagonal entries equal to 1, output only one one containing “-1”.?
Sample Input
2 0 1 1 0 2 1 0 1 0Sample Output
1 R 1 2 -1解題報告:
? ? 看到數(shù)據(jù)量,猜到二分圖。然后仔細分析二分圖確實是可解的,正好二分圖自帶記錄路徑,,可能還是不是很熟練吧第一反應(yīng)沒有建模成二分圖。左側(cè)是行,右側(cè)是列,然后跑二分圖就可以了、題目沒說求最短的變換次數(shù),只是說一個specialjudge唯一要求是小于1000的變換次數(shù)。所以用二分圖找到之后,那最多就是變換n次嘛(但是也有可能小于n次,比如第一個樣例,就直接換了一次湊出倆來,嚴謹來講(n/2)<=cnt<=n),n<=100,所以肯定滿足題意啊。如果說求最少的變換次數(shù),那就應(yīng)該先把(ans[i]== j && ans[j] == i)的找出來,然后再遍歷剩下的?(但是感覺好像和直接遍歷的? 次數(shù)差不多啊。。但是不太會證明、、)
AC代碼:(140ms)
#include<bits/stdc++.h>using namespace std; const int MAX = 100 + 5; bool used[MAX]; int line[MAX][MAX],nxt[MAX],ans[MAX],n,cnt; bool find(int x) {for(int i = 1; i<=n; i++) {if(line[x][i] && used[i] == 0) {used[i]=1;if(nxt[i] == 0 || find(nxt[i])) {nxt[i] = x;return 1;}}}return 0; } int main() {while(~scanf("%d",&n)) {cnt = 0;memset(line,0,sizeof line);memset(ans,0,sizeof ans);memset(nxt,0,sizeof nxt);for(int i = 1; i<=n; i++) {for(int j = 1; j<=n; j++) {scanf("%d",&line[i][j]);}}for(int i = 1; i<=n; i++) {memset(used,0,sizeof used);if(find(i)) cnt++;}if(cnt != n) {puts("-1");continue;}cnt = 0;for(int i = 1; i<=n; i++) {ans[nxt[i]] = i;//ans[i] = j 第i列應(yīng)該等于第j列 //或者說 第i行換到第j行 }for(int i = 1; i<=n; i++) {if(ans[i] != i) {//如果這行需要換for(int j = i+1; j<=n; j++) {if(ans[j] == i) {swap(ans[i],ans[j]);cnt++;}}}}printf("%d\n",cnt);for(int i = 1; i<=n; i++) {ans[nxt[i]] = i;//ans[行] = 列 }for(int i = 1; i<=n; i++) {if(ans[i] != i) {for(int j = i+1; j<=n; j++) {if(ans[j] == i) {printf("R %d %d\n",i,j);swap(ans[i],ans[j]);}}}}} return 0 ;}總結(jié):
?? ??? ?for(int i = 1; i<=n; i++) {
?? ??? ??? ?ans[nxt[i]] = i;//ans[行] = 列?
?? ??? ?}
這一行作用是:ans[i]=j,表示第i行應(yīng)該換到第j行。(你如果最后輸出是C的,也就是換列的,那就不需要這個轉(zhuǎn)換了)
還有一點值得注意,我們的思路不是 如果第i行需要換(即ans[i]=j),則把i和j交換,并且用一個bk數(shù)組標記第j行已經(jīng)完成,再從1找第一個bk=0的行,然后再進行交換。(這樣會使代碼寫起來復(fù)雜。而且每次都得從i=1開始找,因為有可能ans[i]=j其中j<i?不知道會不會有這種可能性)(但是時間復(fù)雜度是不會變的,因為每次都會有一個行被確定下來)
當(dāng)然,也可以AC,下面是部分代碼:(140ms)
for(int i = 1; i<=n; i++) {ans[nxt[i]] = i;//ans[行] = 列 }int flag = 0;while(1) {flag = 0;for(int i = 1; i<=n; i++) {if(ans[i] != i) {flag = 1;int j = ans[i];printf("R %d %d\n",i,j);swap(ans[i],ans[j]);break;}} if(flag == 0) break;}但是,我們不妨換個思路,掃到這行i之后,然后現(xiàn)在我們的想法是要把這樣固定下來,也就是,從后面的行中找到一行假設(shè)為j,使得ans[j] = i , 也就是用第j行來確定第i行,然后這兩行交換,這樣一個for循環(huán)掃每一行就可以了,掃到一行,就從后面的行中找一行來確定這一行。就ok了。至于為什么從i+1行開始,是因為第1~(i-1)行,都已經(jīng)是ans[i]=i了呀,這也就是這兩個方法的不同。(前者是找到一個可以更新別的行的行,然后去更新后面的行;后者是枚舉每一行,然后從后面找一行來更新這一行。不過代碼實現(xiàn)起來都是if(ans[i] != i)這一句)
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的*【HDU - 2819】Swap(二分图匹配,输出路径)(待证明:是否是最少交换次数?)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【PAT - 甲级 - 1018】Pub
- 下一篇: 【HDU - 5744 】Keep On