poj1815+sap+枚举+最小割点集

来源:互联网 发布:唱歌声卡软件下载 编辑:程序博客网 时间:2024/05/21 11:10

建图:

1)每个点v变成网络N里的两个点v*和v**,并有一条弧<v*,v**>,容量为1

2)对于图中的每一条无向边<u,v>,在网络N有两条弧<u**,v*>,<v**,u*>,且容量为无穷大

3)另A**为源点,B*为汇点

求出最小割点集数目之后,枚举每一个点,并删除,如果网络N的最小割点数减少,则意味着该点为割点并保存

#include <map>#include <set>#include <queue>#include <stack>#include <math.h>#include <vector>#include <cstdio>#include <string>#include<string.h>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;#define INF 999999999;const int maxn=10000; //边的数量,应该比正常边的数量多两倍const int MAXN=10000;//节点数int cur[MAXN];//当前弧优化int pre[MAXN];//先祖int level[MAXN];//高度int gap[MAXN];//gap优化bool vis[MAXN][MAXN];struct EDGE {int to,cost,next;}edge[maxn];int head[maxn],cnt;bool deleted[maxn];inline void addedge(int a,int b,int c){edge[cnt].to=b;edge[cnt].cost=c;edge[cnt].next=head[a];head[a]=cnt++;edge[cnt].to=a;edge[cnt].cost=0;edge[cnt].next=head[b];head[b]=cnt++;}void build_graph(int n) //节点数(1->n),边数  {cnt=0;memset(head,-1,sizeof(head));for(int i=1;i<=n;i++)//只需要修改一下输入即可if(!deleted[i])addedge(i,i+n,1);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(!deleted[i]&&!deleted[j]&&i!=j&&vis[i][j])addedge(i+n,j,99999);}int sap(int tot,int s,int t)  //无需改动{memset(pre,-1,sizeof(pre));memset(gap,0,sizeof(gap));memset(level,0,sizeof(level));gap[0]=tot;int u=s,ans=0,inf=INF;for(int i=0; i<tot; i++)//初始化当前弧为第一条弧 cur[i]=head[i];while(level[s]<tot){bool flag=0;for(int &i=cur[u];i!=-1;i=edge[i].next) //读取边,并更新当前弧{int v=edge[i].to;if(edge[i].cost>0&&level[u]==level[v]+1){flag=1;if(edge[i].cost<inf)inf=edge[i].cost;pre[v]=u;u=v;if(u==t){while(u!=s) //结束后u==s{u=pre[u];edge[cur[u]].cost-=inf;edge[cur[u]^1].cost+=inf;//异或是找与其配对的边}ans+=inf;inf=INF;}break;}}if(!flag){if(--gap[level[u]]==0)//gap优化break;int minn=tot;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(edge[i].cost>0&&level[v]<minn){minn=level[v];cur[u]=i;//更新当前弧,让其从度最小的线段开始}}level[u]=minn+1;gap[minn+1]++;if(u!=s)u=pre[u];}}return ans;}int main(){int n,s,t;while(~scanf("%d %d %d",&n,&s,&t)){memset(deleted,0,sizeof(deleted));build_graph(n);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&vis[i][j]);if(vis[s][t]){printf("NO ANSWER!\n");continue;}build_graph(n);int ans=sap(2*n,s+n,t);printf("%d\n",ans); //找出割点数量if(!ans) continue;for(int i=1;i<=n&&ans;i++){if(i==s||i==t)continue;deleted[i]=1;build_graph(n);int kk=sap(2*n,s+n,t);if(kk<ans)           //如果该点为割点,则保存,总的数目也减1ans--;else                 //否则标志恢复deleted[i]=0;}for(int i=1;i<=n;i++)if(deleted[i])printf("%d ",i);printf("\n");}return 0;}


0 0
原创粉丝点击