1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<string> 5 #include<cstring> 6 #include<algorithm> 7 #include<iomanip> 8 #include<queue> 9 using namespace std; 10 namespace Moxing { 11 const int N=105,M=1005; 12 const int inf=0x3f3f3f3f; 13 int n,m,a[N],mp[N][N],id[N][N],idw[M],ict,S,T,cnt=1,last[30303]; 14 long long sum; 15 bool vis[M]; 16 struct node { 17 int to,dis,nxt; 18 } edge[30303<<6]; 19 void add(int from,int to,int dis) { 20 edge[++cnt].to=to,edge[cnt].dis=dis,edge[cnt].nxt=last[from],last[from]=cnt; 21 // edge[++cnt].to=from,edge[cnt].dis=dis,edge[cnt].nxt=last[from],last[from]=cnt; 22 } 23 void insert(int from,int to,int dis) { 24 add(from,to,dis),add(to,from,0); 25 // cout<<from<<‘ ‘<<to<<‘ ‘<<dis<<endl; 26 } 27 void build() { 28 for(int i=1; i<=n; i++) { 29 for(int j=i; j<=n; j++) { 30 id[i][j]=++ict; 31 } 32 } 33 for(int i=1; i<=n; i++) { 34 if(vis[a[i]]) continue ; 35 vis[a[i]]=1,idw[a[i]]=++ict; 36 } 37 // memset(vis,0,sizeof(vis)); 38 S=0,T=ict+n+1; 39 for(int i=1; i<=n; i++) { 40 for(int j=i; j<=n; j++) { 41 if(mp[i][j]>0) {//1 42 insert(S,id[i][j],mp[i][j]);//cout<<‘1‘<<‘ ‘; 43 } else if(mp[i][j]<0) { 44 insert(id[i][j],T,-mp[i][j]);//cout<<‘1‘<<‘ ‘; 45 } 46 insert(id[i][j],ict+i,inf);//2 47 insert(id[i][j],ict+j,inf);//cout<<‘2‘<<‘ ‘; 48 if(i!=j) { //5 49 insert(id[i][j],id[i+1][j],inf); 50 insert(id[i][j],id[i][j-1],inf);//cout<<‘5‘<<‘ ‘; 51 } 52 } 53 } 54 memset(vis,0,sizeof(vis)); 55 for(int i=1; i<=n; i++) { 56 if(vis[a[i]]) continue ; 57 vis[a[i]]=1,insert(idw[a[i]],T,m*a[i]*a[i]);//cout<<‘3‘<<‘ ‘;//3 58 } 59 for(int i=1; i<=n; i++) { 60 insert(ict+i,idw[a[i]],inf);//4 61 insert(ict+i,T,a[i]);//cout<<‘4‘<<‘ ‘; 62 // cout<<"Moxing"; 63 } 64 // cout<<endl; 65 return ; 66 } 67 int d[M*10]; 68 bool bfs() { 69 memset(d,0,sizeof(d)); 70 queue<int>q; 71 d[S]=1; 72 q.push(S); 73 while(!q.empty()) { 74 int x=q.front(); 75 q.pop(); 76 for(int i=last[x]; i; i=edge[i].nxt) { 77 int y=edge[i].to; 78 if(!d[y] && edge[i].dis) { 79 d[y]=d[x]+1; 80 q.push(y); 81 //cout<<y<<‘ ‘; 82 } 83 } 84 } 85 //cout<<d[T]<<‘ ‘; 86 return d[T]; 87 } 88 int dfs(int x,int lim) { 89 if(x==T) return lim; 90 int f=0,tmp; 91 for(int i=last[x]; i; i=edge[i].nxt) { 92 int y=edge[i].to; 93 if(d[y]==d[x]+1&&edge[i].dis&&(tmp=dfs(y,min(lim,edge[i].dis)))) { 94 edge[i].dis-=tmp,edge[i^1].dis+=tmp; 95 lim-=tmp; 96 f+=tmp; 97 if(!lim) return f; 98 } 99 } 100 d[x]=0; 101 return f; 102 } 103 long long dinic() { 104 long long res=0; 105 // cout<<dfs(S,inf)<<‘ ‘; 106 while(bfs()) res+=dfs(S,inf); 107 return res; 108 } 109 struct main { 110 main() { 111 scanf("%d%d",&n,&m); 112 for(int i=1; i<=n; i++) { 113 scanf("%d",&a[i]); 114 } 115 for(int i=1; i<=n; i++) { 116 for(int j=i; j<=n; j++) { 117 scanf("%d",&mp[i][j]); 118 if(mp[i][j]>0) sum+=mp[i][j]; 119 } 120 } 121 build(); 122 long long res=dinic(); 123 // printf("%lld\n",res); 124 sum-=res; 125 printf("%lld\n",sum); 126 exit(0); 127 } 128 } UniversalLove; 129 } 130 int main() { 131 Moxing::main(); 132 }
最大权闭合子图
有一个有向图,每一个点都有一个权值(可以为正或负或0),选择一个权值和最大的子图,使得每个点的后继都在子图里面,这个子图就叫最大权闭合子图。
建图:从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量全部为无限大
答案为正权值之和-最小割
为什么呢?以下引用自:https://www.cnblogs.com/dilthey/p/7565206.html
这时,可以得到小结论:
①该带边权有向图的关于s-t最小割,是简单割;
简单割:割集中所有的边,都与s或t相连接。
显然的,因为不与s,t相连的边,权值都是INF,最小割不可能割在INF的边上;
这早在文章http://www.cnblogs.com/dilthey/p/7401563.html中就有出现提到过,实际上,这个带边权有向图就是二分图;
②该图中的每一个简单割产生的两个子图,我们记含有点s的是图S,含有点t的是图T,则图S是闭合图;
闭合图:在一个图中,我们选取一些点构成集合,若集合中任意点连接的任意出弧,所指向的终点也在V中,则这个集合以及所有这些边构成闭合图。
证明:简单割内不包含边权为INF的边,即不含有连通两个图的边(除了连接在t点上的边之外);
即,图S中没有边与图T连通,那么,所有的边都只能连接在图S之内,即为闭合图。
③最小割产生的图S和图T,图S为最大权闭合子图;
最大权闭合子图:在整个图中,有多个子图是满足闭合图的条件的,其中点权值之和最大的,为最大权闭合子图;
因为割集中所有的边,不是连接在s上,就是连接在t上;
我们记割集中,所有连接在s上的边的权值和为x1,所有连接在t上的边的权值和为x2,而割集中所有边权值和为X=x1+x2;
又,记图S中所有点的权值和为W,记其中正权值之和为w1,负权值之和为 - w2,故W = w1 - w2;
而 W + X = w1 - w2 + x1 + x2,由于x2 = w2
(因为图S中所有负权值的点,必然连接到t点,而图S必然要与t分割开;故割集中,“连接在t点上的边权值和”就是“图S中所有负权值点的权值之和,取负”)
因而W + X = w1 + x1;
而显然的,w1 + x1是整个图中所有正权值之和,记为SUM;
故W = SUM - X,即 “图S中所有点的权值和” = “整个图中所有正权值之和” - “割集中所有边权值和”;
然后,因为SUM为定值,只要我们取最小割,则“图S中所有点的权值和”就是最大的,即此时图S为图S为最大权闭合子图;
最后,我们就有了求解这类问题的完整思路:
①先记录整个图中,所有正点权值的和;
②建立对应流网络,求最大流,最大流在数值上等于最小割,故我们得到了流网络的s-t最小割;
③“所有正点权值的和”减去“s-t最小割”,即得最大权闭合子图的权值和。
原文:https://www.cnblogs.com/Moxingtianxia/p/11370294.html