BZOJ-3894 文理分科 最小割

题面

题意:给定一个n×m的矩阵,每个格子的人可以学文或者学理,学文和学理各有一个满意度,如果以某人为中心的十字内所有人都学文或者学理还会得到一个额外满意度,求最大满意度之和

   给你一个n×m的图,你可以给每个点染1或者0,对于每个点染1或者0都有一个权值的收益,如果一个点周围有公共边的点都选择了1或者0,也会有一个权值的收益,一个给你4个收益矩阵,求最大的收益

 

题解:

  令S为学文,T为学理

  每个人学文就S连向人,学理就人连向T

  如果某个集合内的人都学理会获得一个满意度,那么就新加一个点,将集合内的所有人向这个点连流量为inf的边,再从这个点向T连一条流量为满意度的边,表示集合内任意一个人学文都要把这个点与T的边割掉

  学文同理

  再求最小割就行了


 

        其实,学过二元组建图的话就很容易想到,这个问题不过是把多元关系转换成二元关系就好

   我们对每个节点x新建两个点x0,x1分别表示x周围是否都是0和是否都是1,就有了四种二元关系

   x是0 且 x周围都是0,那么就有v1的收入

   x是1 且 x周围都是1,那么就有v2的收入

   x周围都是0,与x相邻的点必须为0,如果为1,则造成inf的代价

   x周围都是1,与x相邻的点必须为1,如果为0,则造成inf的代价

   然后二元组建图即可

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define N 30005
  4 #define M 300005
  5 #define inf 0x7fffffff/3
  6 namespace Dinic
  7 {
  8     int head[N],head2[N],p=1;
  9     struct rec
 10     {
 11         int go,nex,c;
 12     }eg[M*2];
 13     void build(int a,int b,int c)
 14     {
 15         eg[++p]=(rec){b,head[a],-c};
 16         head[a]=p;
 17         eg[++p]=(rec){a,head[b],0};
 18         head[b]=p;
 19     }
 20     int dis[N],q[N],s[N],S,T,stop,ans;
 21     bool bfs()
 22     {
 23         memset(dis,0,sizeof(dis));
 24         dis[T]=1;
 25         q[1]=T;
 26         for (int p1=1,p2=1;p1<=p2;p1++)
 27         {
 28             for (int i=head[q[p1]];i;i=eg[i].nex)
 29                 if (eg[i^1].c<0 && !dis[eg[i].go])
 30                 {
 31                     dis[eg[i].go]=dis[q[p1]]+1;
 32                     q[++p2]=eg[i].go;
 33                 }
 34         }
 35         if (!dis[S]) return 0;
 36         memcpy(head2,head,sizeof(head));
 37         return 1;
 38     }
 39     bool dinic(int p,int top)
 40     {
 41         if (p==T)
 42         {
 43             int x=inf;
 44             for (int i=1;i<=top-1;i++) if (-eg[s[i]].c<x) x=-eg[s[i]].c,stop=i;
 45             for (int i=1;i<=top-1;i++) eg[s[i]].c+=x,eg[s[i]^1].c-=x;
 46             ans+=x;
 47             return 1;
 48         }
 49         for (int &i=head2[p];i;i=eg[i].nex)
 50         {
 51             if (eg[i].c<0 && dis[eg[i].go]==dis[p]-1)
 52             {
 53                 s[top]=i;
 54                 if (dinic(eg[i].go,top+1) && top!=stop) return 1;
 55             }
 56         }
 57         return 0;
 58     }
 59     int ask()
 60     {
 61         ans=0;
 62         while (bfs()) dinic(S,1);
 63         return ans;
 64     }
 65     void init(int _S,int _T)
 66     {
 67         S=_S;
 68         T=_T;
 69     }
 70 }
 71 using namespace Dinic;
 72 int n,m,a,x,y,id[102][102],anss;
 73 int ss,tt;
 74 int dx[5]={0,0,1,-1};
 75 int dy[5]={1,-1,0,0};
 76 int main()
 77 {
 78     scanf("%d%d",&n,&m);
 79     int why=0;
 80     ss=0;tt=n*m+3+1;
 81     for (int i=1;i<=n;i++)
 82     for (int j=1;j<=m;j++) id[i][j]=++why;
 83     for (int i=1;i<=n;i++)
 84     for (int j=1;j<=m;j++) 
 85     {
 86         scanf("%d",&a);
 87         anss+=a;
 88         build(ss,id[i][j],a);
 89     }
 90     for (int i=1;i<=n;i++)
 91     for (int j=1;j<=m;j++) 
 92     {
 93         scanf("%d",&a);
 94         anss+=a;
 95         build(id[i][j],tt,a);
 96     }    
 97     
 98     for (int i=1;i<=n;i++)
 99     for (int j=1;j<=m;j++)
100     {
101         scanf("%d",&a);
102         anss+=a;
103         build(ss,id[i][j]+m*n,a);
104         for (int k=0;k<5;k++)
105         {
106             x=i+dx[k];
107             y=j+dy[k];
108             if (x<=0 || y<=0 || x>n || y>m) continue;
109             build(id[i][j]+m*n,id[x][y],inf);
110         }
111     }
112     for (int i=1;i<=n;i++)
113     for (int j=1;j<=m;j++)
114     {
115         scanf("%d",&a);
116         anss+=a;
117         build(id[i][j]+2*m*n,tt,a);
118         for (int k=0;k<5;k++)
119         {
120             x=i+dx[k];
121             y=j+dy[k];
122             if (x<=0 || y<=0 || x>n || y>m) continue;
123             build(id[x][y],id[i][j]+2*m*n,inf);
124         }
125     }
126     init(ss,tt);
127     printf("%d\n",anss-ask());        
128 }

 

posted @ 2018-09-26 16:08  口香糖万岁  阅读(142)  评论(0编辑  收藏  举报