JZOJ 5934. 【NOIP2018模擬10.29】列隊
阿新 • • 發佈:2018-12-18
題目
一個n*n的方陣,有m個關鍵點,一次只能夠選擇hack一行或一列的點,每行或每列最多隻能夠被hack一次,每個關鍵點最多只能夠被hack一次。 求最多能夠hack多少次。 n<=1000,m<=4000.
題解
看錯題了。看錯2次。 第一次看錯題,以為hack一次是指hack一排點。一次hack只能夠hack一個點。 第二次看錯題,以為一個點至少要被hack一次。 如果是這樣子的話,兩排點直接並查集就好了。然後每個連通塊要麼選行,要麼選列。 實際上,答案是最大獨立集的個數. 最大獨立集,也就是所有頂點數-最小頂點覆蓋。最大獨立集中,點兩兩之間沒有邊。 最小頂點覆蓋,用這些點就能夠覆蓋完所有的邊。 匈牙利演算法的時間複雜度 菜雞應該重講匈牙利演算法。 對於每個點,開始找增廣路,如果能夠找到就繼續找,匹配的邊變成未匹配的,未匹配的邊變成匹配的。 在從起點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;
}