1. 程式人生 > >JZOJ 5934. 【NOIP2018模擬10.29】列隊

JZOJ 5934. 【NOIP2018模擬10.29】列隊

題目

一個n*n的方陣,有m個關鍵點,一次只能夠選擇hack一行或一列的點,每行或每列最多隻能夠被hack一次,每個關鍵點最多只能夠被hack一次。 求最多能夠hack多少次。 n<=1000,m<=4000.

題解

看錯題了。看錯2次。 第一次看錯題,以為hack一次是指hack一排點。一次hack只能夠hack一個點。 第二次看錯題,以為一個點至少要被hack一次。 如果是這樣子的話,兩排點直接並查集就好了。然後每個連通塊要麼選行,要麼選列。 實際上,答案是最大獨立集的個數. 最大獨立集,也就是所有頂點數-最小頂點覆蓋。最大獨立集中,點兩兩之間沒有邊。 最小頂點覆蓋,用這些點就能夠覆蓋完所有的邊。 匈牙利演算法的時間複雜度O

(nm)O(nm) 菜雞應該重講匈牙利演算法。 對於每個點,開始找增廣路,如果能夠找到就繼續找,匹配的邊變成未匹配的,未匹配的邊變成匹配的。 在從起點x開始尋找增廣路的過程中,每個點最多隻會在增廣路里出現1次。所以用bool陣列判斷一下就好,但考慮到時間複雜度問題,將bool陣列換成染色陣列即可。

匈牙利演算法程式碼

bool find(int x){
    int i;
    bz[x]=j;
    for(i=head[x];i;i=edge[i].next)
        if(!pre[edge[i].to]||(bz[pre[edge[i].to]]!=j&&
find(pre[edge[i].to]))){ pre[edge[i].to]=x; return 1; } return 0; }

嚴肅檢討

為什麼會看錯題? 一開始由於看錯了第一次題,就有一點慌了,因為放在了NOIP第一題的位置。 一般8:45就看完3道題目了,至少看完第一題了吧。就是一點點意思沒有get準,結果就出了大問題。 HOW TO DO WITH?

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm> #define N 2010 #define M 4010 #define P(a) putchar(a) #define fo(i,a,b) for(i=a;i<=b;i++) using namespace std; struct note{ int to,next; }edge[M]; int tot,head[N]; int pre[N],bz[N]; int n,m,i,j,lim,ans; int x,y; int read(){ int fh=0,rs=0;char ch=0; while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar(); if(ch=='-')fh=1,ch=getchar(); while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar(); return fh?-rs:rs; } void write(int x){ if(x>9)write(x/10); P(x%10+'0'); } bool find(int x){ int i; bz[x]=j; for(i=head[x];i;i=edge[i].next) if(!pre[edge[i].to]||(bz[pre[edge[i].to]]!=j&&find(pre[edge[i].to]))){ pre[edge[i].to]=x; return 1; } return 0; } void lb(int x,int y){ edge[++tot].to=y; edge[tot].next=head[x]; head[x]=tot; } int main(){ n=read(),m=read(); fo(i,1,m){ x=read(),y=read(); lb(x,y+n); } fo(j,1,n)if(find(j))ans++; ans=((n<<1)-ans)*n; write(ans); return 0; }