HDU - 3026 Chinese Chess(二分图的必经边)
題目鏈接:點擊查看
題目大意:給出一個n*m的棋盤,現在開始放 ‘車’ 棋子,規定只有k個位置可以擺放棋子,現在要求擺放的棋子盡可能多且不能互相攻擊,到此為止是一個經典的二分圖最大匹配問題,接下來是要求有多少個位置,是必須放置的,如果某個點刪去后,最大匹配減少了,那么說明該點為必須放置棋子的點,現在題目要求輸出有多少個必須放置的點,以及最大匹配
題目分析:如果沒看懂題可以先去看看HDU1281的題面,是中文題面:點擊查看
這個題就是HDU1281的數據加強版,N和M的數量級翻了個翻,數據較弱的點直接用匈牙利跑二分匹配,匹配完后枚舉每一個頂點,刪除后再跑匈牙利,如果最大匹配減少了,則說明刪去的點為必經點,當然這樣毫無頭腦的暴力是可以過掉那個數據較弱的題目的
其實還有更好的方法可以優化,因為我們只需要找一下是否有增廣路就可以了,所以并不需要從零開始跑匈牙利,換句話說,我們可以從找必經點,轉換為找必經邊,首先拿一下大藍書上的定義:
(x,y)是必經邊,當且僅當一下兩個條件都滿足:
(x,y)是可行邊,當且僅當滿足以下兩個條件之一:
這樣一來我們就能將跑匈牙利的操作轉換為找增廣路的操作了,如何簡單有效的實現上述操作呢?大藍書上給出了比較好的方法,雖然不太理解,但是會用就好了。。卑微的菜雞
在跑完匈牙利后,我們對于匹配邊置反,未匹配邊不變,跑強連通縮點,如此一來可以直接使用以下結論:
這樣一來因為這個題目的數據較大,我們可以直接用最大流來跑二分圖最大匹配,跑完后在殘余網絡上流量為1的點表示著上述的匹配邊置反以及未匹配邊,直接強連通縮點后根據結論統計就好了
代碼:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=2e4+100;const int M=3e5+100;struct Edge {int to,w,next; }edge[M];//邊數int low[N],dfn[N],c[N],Stack[N],num,cnt,cnt2,cnt1,dcc,n,m,k,top,head[N],tot;bool ins[N];vector<int>scc[N];void addedge(int u,int v,int w=0) {edge[tot].to=v;edge[tot].w=w;edge[tot].next=head[u];head[u]=tot++;edge[tot].to=u;edge[tot].w=0;//反向邊邊權設置為0edge[tot].next=head[v];head[v]=tot++; }void tarjan(int u) {dfn[u]=low[u]=++num;Stack[++top]=u;ins[u]=true;for(int i=head[u];i!=-1;i=edge[i].next){if(!edge[i].w)continue;int v=edge[i].to;if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(ins[v])low[u]=min(low[u],dfn[v]);}if(dfn[u]==low[u]){cnt++;int v;do{v=Stack[top--];ins[v]=false;c[v]=cnt;scc[cnt].push_back(v);}while(u!=v);} }void solve() {for(int i=1;i<=n;i++)//縮點 if(!dfn[i])tarjan(i); }int d[N],now[N];//深度 當前弧優化bool bfs(int s,int t)//尋找增廣路 {memset(d,0,sizeof(d));queue<int>q;q.push(s);now[s]=head[s];d[s]=1;while(!q.empty()){int u=q.front();q.pop();for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;if(d[v])continue;if(!w)continue;d[v]=d[u]+1;now[v]=head[v];q.push(v);if(v==t)return true;}}return false; }int dinic(int x,int t,int flow)//更新答案 {if(x==t)return flow;int rest=flow,i;for(i=now[x];i!=-1&&rest;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;if(w&&d[v]==d[x]+1){int k=dinic(v,t,min(rest,w));if(!k)d[v]=0;edge[i].w-=k;edge[i^1].w+=k;rest-=k;}}now[x]=i;return flow-rest; }void init() {memset(now,0,sizeof(now));memset(head,-1,sizeof(head));for(int i=0;i<N;i++)scc[i].clear();tot=top=cnt=num=dcc=0;memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));memset(c,0,sizeof(c));memset(ins,false,sizeof(ins)); }int solve(int st,int ed) {int ans=0,flow;while(bfs(st,ed))while(flow=dinic(st,ed,inf))ans+=flow;return ans; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int kase=0;while(scanf("%d%d%d",&n,&m,&k)!=EOF){init();int st=N-1,ed=st-1;while(k--){int u,v;scanf("%d%d",&u,&v);addedge(u,v+n,1);}for(int i=1;i<=n;i++)addedge(st,i,1);for(int i=1;i<=m;i++)addedge(i+n,ed,1);int ans1=solve(st,ed);solve();int ans2=0;for(int u=1;u<=n;u++){for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;int w=edge[i].w;if(v==st)continue;if(c[u]!=c[v]&&!w)ans2++;}}printf("Board %d have %d important blanks for %d chessmen.\n",++kase,ans2,ans1);}return 0; }?
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的HDU - 3026 Chinese Chess(二分图的必经边)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 1220D A
- 下一篇: CodeForces - 1220E T