題目:點擊開啟連結
網路流學習的下一步,最大點權獨立集。多和最小點權覆蓋集放到一起使用。分別求圖中不相鄰的點權最大/或者邊中至少有一點在集合中,求最小總權值的問題。
公式:
最大點權獨立集=總權值-最小點權覆蓋集。
最小點權覆蓋集=圖的最小割值=最大流。和普通網路流一樣,可以使用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; }