[网络流24题] 方格取数问题

~~~题面~~~

题解:

我们观察到题目要求相邻的格子只能选一个,那么我们能想到什么呢?

最大点权独立集!

但是怎么建图?

我们首先对格子进行黑白染色,这样就构成了一个二分图,染色后连边。

1,s ---> 白色, w = 权值

2,黑色 ---> t , w = 权值

3,白色 ---> 黑色 , w = inf

连完之后跑网络流即可。

ans = 权值和 - 最大流

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define AC 10000+200
  4 #define ACway 60000+600 //~~~~~~此处为了方便调试已经缩小一百倍,记得修改 
  5 int Head[AC],good[AC],have[AC],q[AC],head,tail,tot,s,t,m,M[110][110],n,x,ans,addflow,c[AC],last[AC],key;
  6 int date[ACway],Next[ACway],haveflow[ACway];
  7 bool co[110][110];
  8 int Min(int a,int b)
  9 {
 10     int c=(b-a)>>31;
 11     return (a&~c)|(b&c);
 12 }
 13 void add(int f,int w,int S)
 14 {
 15     //if(f==t&&w==s)printf("``%d\n",tot);
 16 //    printf("@@%d %d %d\n",f,w,s);
 17     date[++tot]=w;
 18     haveflow[tot]=S;
 19     Next[tot]=Head[f];
 20     Head[f]=tot;
 21 }
 22 int read()
 23 {
 24     int x=0;char c=getchar();
 25     while(c>'9'||c<'0')c=getchar();
 26     while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
 27     return x;
 28 }
 29 void pre()
 30 {
 31     int now;
 32     tot=1;
 33     n=read(),m=read();
 34     if(!(m%2))key=m+1;
 35     else key=m;//强行扩充为奇数列
 36     //要把整个图分成2个部分,一个放在前驱集合,一个放在后继集合,那么一共n*m+2个点
 37     s=n*key+1,t=n*key+2;
 38     for(int i=1;i<=n;i++)//读入并初始化ans
 39         for(int j=1;j<=m;j++)
 40         {
 41             M[i][j]=read();
 42             ans+=M[i][j];
 43         }
 44     for(int i=1;i<=n;i++)
 45         for(int j=1;j<=m;j++)
 46         {
 47             now=(i-1)*key+j;//(i-1)代表上面有多少行,所以应该要乘列数,然后加上这行的
 48             //printf("%d %d\n",i,j);
 49             if(now%2)//因为是把整个图给分开了,所以点也是分成两部分,
 50             {
 51                 //由于将列数强行扩充到奇数,所以相邻的点奇偶性会不同
 52                 //又因为只有相邻才冲突,所以只有奇偶不同才连边,
 53                 //这里把奇数放在前驱集合,也就是说只有奇数会向外连边(防止重复)
 54                 if(i!=n)//如果不是最后一行就向下连边
 55                 {
 56                     add(now,now+key,INT_MAX);
 57                     add(now+key,now,0);
 58                 }
 59                 if(i!=1)
 60                 {
 61                     add(now,now-key,INT_MAX);
 62                     add(now-key,now,0);
 63                 }
 64                 if(j!=m)//如果不是最后一列就向右连边
 65                 {
 66                     add(now,now+1,INT_MAX);//仅仅起到连接作用,控制边权依靠s和t
 67                     add(now+1,now,0);
 68                 }
 69                  if(j!=1)//为防止漏边,是最后一列就向左连边,上同
 70                 {
 71                     add(now,now-1,INT_MAX);
 72                     add(now-1,now,0);
 73                 }
 74                 add(s,now,M[i][j]);//添加每个点和源点的连边
 75                 add(now,s,0);
 76             }
 77             else
 78             {
 79                 add(now,t,M[i][j]);//添加每个点和后继集合的连边
 80                 add(t,now,0);
 81             }
 82         }
 83 }
 84 void bfs()
 85 {
 86     int x=t,now;
 87     c[t]=1,q[++tail]=t,have[1]=1;
 88     memcpy(good,Head,sizeof(Head));
 89     while(head<tail)
 90     {
 91         x=q[++head];
 92         for(int i=Head[x]; i ;i=Next[i])
 93         {
 94             now=date[i];
 95             if(!c[now] && haveflow[i^1])
 96             {
 97                 c[now]=c[x]+1;
 98                 q[++tail]=now;
 99                 have[c[now]]++;
100             }
101         }
102     }
103 }
104 void aru()
105 {
106  //   printf("%d <--- ",t);
107     while(x!=s)
108     {
109         haveflow[last[x]]-=addflow;
110         haveflow[last[x]^1]+=addflow;
111         x=date[last[x]^1];
112    //     printf("%d <--- ",x);
113     }
114   //  printf("the addflow is %d ",addflow);
115    // printf("\n");
116     ans-=addflow;
117 
118 }
119 void isap()
120 {
121     bool done;int now;
122     x=s,addflow=INT_MAX;
123     while(c[s]!=n*key+3)//~~~~~~~~记得修改
124     {
125         if(x==t)aru(),addflow=INT_MAX;
126         done=false;
127         for(int i=good[x]; i ;i=Next[i])
128         {
129             now=date[i];
130             if(haveflow[i] && c[now]==c[x]-1)
131             {
132                 good[x]=i;
133                 done=true;
134                 addflow=Min(addflow,haveflow[i]);
135                 last[now]=i;
136                 x=now;
137                 break;
138             }
139         }
140         if(!done)
141         {
142             int go=n*key+2;//记得修改
143             for(int i=Head[x]; i ;i=Next[i])//Head[x]!!!!!!!!!!!!!!!!!!!
144             {
145                 if(haveflow[i] && c[date[i]])go=Min(go,c[date[i]]);
146             }
147             good[x]=Head[x];
148             if(!(--have[c[x]]))break;
149             have[c[x]=go+1]++;
150             if(x!=s)x=date[last[x]^1];
151         }
152     }
153     printf("%d\n",ans);
154 }
155 int main()
156 {
157     pre();
158     bfs();
159     isap();
160    // printf("%d\n",key);
161     return 0;
162 }

猜你喜欢

转载自www.cnblogs.com/ww3113306/p/9367086.html