BZOJ 4950 [Wf 2017] 二分圖最大匹配 解題報告
Description
那是春日裡一個天氣晴朗的好日子,你準備去見見你的老朋友Patrick,也是你之前的犯罪同夥。Patrick在程式設計競賽上豪賭輸掉了一大筆錢,所以他需要再幹一票。為此他需要你的幫助,雖然你已經金盆洗手了。你剛開始很不情願,因為你一點也不想再回到那條老路上了,但是你覺得聽一下他的計劃也無傷大雅。在附近的一個倉庫裡有一批貨物,包含一些貴重的消費性部件,Patrick企圖從中儘可能多地偷些東西出來。這意味著要找一條進去的路,弄暈安保人員,穿過各種各樣的鐳射射線,你懂的,都是常見的搶劫技術。然而,倉庫的核心裝備了一套Patrick搞不定的安保系統。這也是他需要你幫助他的地方。這批貨物被放置在一些巨大的立方體箱裡,每個箱子的尺寸都是相同的。這些箱子堆放成許多整齊的堆,每個箱子可以表示成一個三維的網格。安保系統每個小時會用三臺相機對這堆貨物進行一次拍照,相機分別為:前置相機(front camera),側置相機(side camera)和頂置相機(top camera)。前置相機的照片顯示了每一行最高的那堆箱子的高度,側置相機顯示了每一列最高的那堆箱子的高度,頂置相機顯示了每個位置是否存在一堆箱子。如果安保系統發現任何一張照片出現了變化,它會立即拉響警報。一旦 Patrick 進去了,他會確定每堆箱子的高度並且發給你。
Patrick想盡可能多偷走一些箱子。由於他不能弄壞安保系統,他準備重新安排剩餘每堆箱子的放置,使得一次相機取像時會得到相同的照片,從而騙過安保系統。在上面的例子中,他可以偷走九個箱子。圖2顯示了一種可能的剩餘箱子的安置方案能使得安保系統認為與原安置情況相同。Patrick想請你幫他確定在保證能騙過安保系統的情況下他最多能偷走多少個箱子。你會幫他幹完這最後一票麼?
Input
第一行包含兩個整數r(1≤r≤100)和c(1≤n≤100),分別表示網格的行數與列數。
接下來r行,每行包含c個整數,表示對應行上每堆立方體箱的高度(箱子的數量)。
所有的高度在0到10^9之間 (含邊界) 。
Output
輸出在不被發現的情況下最多能偷走多少箱子。
Sample Input
樣例1
5 5
1 4 0 5 2
2 1 2 0 1
0 2 3 4 4
0 3 0 3 1
1 2 2 1 1
樣例2
2 3
50 20 3
20 10 3
Sample Output
樣例1
9
樣例2
30
【解題報告】
程式碼如下:
/**************************************************************
Problem: 4950
User: onepointo
Language: C++
Result: Accepted
Time:48 ms
Memory:1780 kb
****************************************************************/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 210
#define M 100010
#define LL long long
int n,m,cnt=-1,head[N];
struct Edge{int to,nxt;}e[M];
int vis[N],match[N];
int mp[N][N],ml[N],mh[N];
LL sum=0;
void adde(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool dfs(int u,int flag)
{
for(int i=head[u];~i;i=e[i].nxt)
{
int v=e[i].to;
if(vis[v]==flag) continue;
vis[v]=flag;
if(!match[v]||dfs(match[v],flag))
{
match[v]=u;
return 1;
}
}
return 0;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
scanf("%d",&mp[i][j]);
mh[i]=max(mh[i],mp[i][j]);
ml[j]=max(ml[j],mp[i][j]);
if(mp[i][j]) sum+=mp[i][j]-1;
}
for(int i=1;i<=n;++i) if(mh[i]) sum-=mh[i]-1;
for(int i=1;i<=m;++i) if(ml[i]) sum-=ml[i]-1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(mh[i]==ml[j]&&mp[i][j])
adde(i,j+n);
}
for(int i=1;i<=n;++i)
{
if(dfs(i,i)) sum+=mh[i]-1;
}
printf("%lld\n",sum);
return 0;
}