题目:点击打开链接
网络流学习的下一步,最大点权独立集。多和最小点权覆盖集放到一起使用。分别求图中不相邻的点权最大/或者边中至少有一点在集合中,求最小总权值的问题。
公式:
最大点权独立集=总权值-最小点权覆盖集。
最小点权覆盖集=图的最小割值=最大流。和普通网络流一样,可以使用ISAP,EK,DINIC等多种方法。
建图:
多采用奇偶建立二分图的方法。建边时脑子一定要清楚。相邻点要染不同的颜色,源点为0,汇点为最后的那个点。染色方法基本上是判断if(边+边)%2==0源点与其连接,反之汇点与其连接。如果两点相邻的话,连接值为INF,无穷大。
脑子一定要清醒。。wa了若干次,用的是最快的sap.
#include <iostream> #include <cstring> #include <string> #include <iomanip> using namespace std; typedef struct {int v,next,val;} edge; const int INF=0x3F3F3F3F; const int MAXN=2000; const int MAXM=50000; edge e[MAXM]; int p[MAXN],eid; inline void init(){memset(p,-1,sizeof(p));eid=0;} //有向 inline void insert1(int from,int to,int val) { e[eid].v=to; e[eid].val=val; e[eid].next=p[from]; p[from]=eid++; swap(from,to); e[eid].v=to; e[eid].val=0; e[eid].next=p[from]; p[from]=eid++; } //无向 inline void insert2(int from,int to,int val) { e[eid].v=to; e[eid].val=val; e[eid].next=p[from]; p[from]=eid++; swap(from,to); e[eid].v=to; e[eid].val=val; e[eid].next=p[from]; p[from]=eid++; } int n,m;//n为点数 m为边数 int h[MAXN]; int gap[MAXN]; int source,sink; inline int dfs(int pos,int cost) { if (pos==sink) { return cost; } int j,minh=n-1,lv=cost,d; for (j=p[pos];j!=-1;j=e[j].next) { int v=e[j].v,val=e[j].val; if(val>0) { if (h[v]+1==h[pos]) { if (lv<e[j].val) d=lv; else d=e[j].val; d=dfs(v,d); e[j].val-=d; e[j^1].val+=d; lv-=d; if (h[source]>=n) return cost-lv; if (lv==0) break; } if (h[v]<minh) minh=h[v]; } } if (lv==cost) { --gap[h[pos]]; if (gap[h[pos]]==0) h[source]=n; h[pos]=minh+1; ++gap[h[pos]]; } return cost-lv; } int sap(int st,int ed) { source=st; sink=ed; int ret=0; memset(gap,0,sizeof(gap)); memset(h,0,sizeof(h)); gap[st]=n; while (h[st]<n) { ret+=dfs(st,INF); } return ret; } int main() { int pt,ma; while(cin>>pt) { int tmp; init(); n=2000; m=10000; int sum=0; //最大点权独立集=总权值-最小点权覆盖集=总权值-最小割 int st=0; //start point int ed=(pt*pt)+1; for(int i=1;i<=pt;i++) { for(int j=1;j<=pt;j++) { scanf("%d",&tmp); sum+=tmp; if((i+j)%2==0) //偶数序号点 { insert1(st,(i-1)*pt+j,tmp); if(i>1) insert1((i-1)*pt+j,(i-2)*pt+j,INF); if(j>1) insert1((i-1)*pt+j,(i-1)*pt+j-1,INF); //顺序一定不能错 if(i<pt) insert1((i-1)*pt+j,(i)*pt+j,INF); if(j<pt) insert1((i-1)*pt+j,(i-1)*pt+j+1,INF); } else insert1((i-1)*pt+j,ed,tmp); } } int res=sap(st,ed); cout<<sum-res<<endl; } return 0; }