Poj 3041 Asteroids (二分圖最小點覆蓋)
阿新 • • 發佈:2018-12-15
題意:給一個500*500的網格圖,格點上有一些點,每次可以消除一行或者一列上所有的點,問消除所有的點最少需要幾次。
思路:因為要消除一個點,只有2種方式,並且一行或者一列多次消除是沒有意義的。可以考慮構建一個二分圖,左邊是所有的行,右邊是所有的列,把待消除的點作為邊,一個點會唯一連一條邊,那麼題意轉化為了,選擇二分圖上的點,把與這個點連的邊加入一個集合,這個集合應該包含了所有邊,最少選擇的點的個數,這個問題轉化為了最小點覆蓋。
最小點覆蓋是一個NP困難的問題,但在二分圖裡它等於最大匹配數,因此跑一遍最大流即可。
(Poj評測環境next是作為保留字,不能作為變數使用,並且不支援在結構體裡初始化。)
#include<queue> #include<cstdio> #include<cstring> //#include<bits/stdc++.h> using namespace std; const int maxn=1100; const int maxm=260000; const int inf=1e9; int cnt=-1,n,k,s,t; int head[maxn],dep[maxn],cur[maxn]; struct Edge { int nxt; int to,w; } edge[maxm*2]; void add_edge(int u,int v,int w) { edge[++cnt].nxt=head[u]; edge[cnt].to=v; edge[cnt].w=w; head[u]=cnt; } bool bfs() { queue<int>que; while(!que.empty())que.pop(); memset(dep,0,sizeof(dep)); dep[s]=1; que.push(s); while(!que.empty()) { int u=que.front(); que.pop(); for(int i=head[u]; i!=-1; i=edge[i].nxt) { int v=edge[i].to; if(!dep[v]&&edge[i].w) { dep[v]=dep[u]+1; que.push(v); } } } if(dep[t]>0) return 1; return 0; } int dfs(int u,int flow) { if(u==t) return flow; for(int &i=cur[u]; i!=-1; i=edge[i].nxt) { int v=edge[i].to; if((dep[v]==dep[u]+1)&&edge[i].w) { int d=dfs(v,min(flow,edge[i].w)); if(d>0) { edge[i].w-=d; edge[i^1].w+=d; return d; } } } return 0; } int Dinic() { int ans=0; while(bfs()) { for(int i=1; i<=2*n+2; i++) cur[i]=head[i]; while(int d=dfs(s,inf)) { ans+=d; } } return ans; } int main() { //建圖 左邊點1-N(行) 右邊點N+1, 2N 源點2N+1,匯點2N+2; for(int i=0;i<maxm*2;i++) edge[i].nxt=-1; scanf("%d %d",&n,&k); memset(head,-1,sizeof(head)); cnt=-1; for(int i=1;i<=k;i++) { int x,y; scanf("%d %d",&x,&y); add_edge(x,y+n,1); add_edge(y+n,x,0); } for(int i=1;i<=n;i++) { add_edge(2*n+1,i,1); add_edge(i,2*n+1,0); add_edge(i+n,2*n+2,1); add_edge(2*n+2,i+n,0); } s=2*n+1; t=2*n+2; printf("%d\n",Dinic()); }