HDU3657 Game(最小割)
2016-03-23 21:49
281 查看
题目大概说,给一个n×m的格子,每个格子都有数字,选择一个格子就能加上格子数字的分数,有k个格子必须选择,如果两个相邻的格子都被选择了那分数要减去两个格子数字的与再乘2。问能取得的最大分数。
已经知道这题是最小割。。黑白染色,画了下图,觉得很有道理,然后写了写就AC了。。具体建图是这样的:
对格子进行黑白染色形成二分图,源点向X部的点连容量为选该点能获得分数的边,Y部的点向汇点连容量为选该点能获得分数的边,对于必须选择的点则连容量INF的边
对于X部与Y部在格子中相邻的点,从X部的点向Y部的点连容量为同时选择两点失去的分数,即二者的与再乘2
这样S-T的割边集的权和就是失去的分数,包括同时选相邻的两点失去的分数以及不选择某点不能得到的分数。于是要求的结果就是格子中所有点的数字和-最小割。
不过如果不知道这题是最小割。。我好像想不到。
已经知道这题是最小割。。黑白染色,画了下图,觉得很有道理,然后写了写就AC了。。具体建图是这样的:
对格子进行黑白染色形成二分图,源点向X部的点连容量为选该点能获得分数的边,Y部的点向汇点连容量为选该点能获得分数的边,对于必须选择的点则连容量INF的边
对于X部与Y部在格子中相邻的点,从X部的点向Y部的点连容量为同时选择两点失去的分数,即二者的与再乘2
这样S-T的割边集的权和就是失去的分数,包括同时选相邻的两点失去的分数以及不选择某点不能得到的分数。于是要求的结果就是格子中所有点的数字和-最小割。
不过如果不知道这题是最小割。。我好像想不到。
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; #define INF (1<<30) #define MAXN 2555 #define MAXM 2555*222 struct Edge{ int v,cap,flow,next; }edge[MAXM]; int vs,vt,NE,NV; int head[MAXN]; void addEdge(int u,int v,int cap){ edge[NE].v=v; edge[NE].cap=cap; edge[NE].flow=0; edge[NE].next=head[u]; head[u]=NE++; edge[NE].v=u; edge[NE].cap=0; edge[NE].flow=0; edge[NE].next=head[v]; head[v]=NE++; } int level[MAXN]; int gap[MAXN]; void bfs(){ memset(level,-1,sizeof(level)); memset(gap,0,sizeof(gap)); level[vt]=0; gap[level[vt]]++; queue<int> que; que.push(vt); while(!que.empty()){ int u=que.front(); que.pop(); for(int i=head[u]; i!=-1; i=edge[i].next){ int v=edge[i].v; if(level[v]!=-1) continue; level[v]=level[u]+1; gap[level[v]]++; que.push(v); } } } int pre[MAXN]; int cur[MAXN]; int ISAP(){ bfs(); memset(pre,-1,sizeof(pre)); memcpy(cur,head,sizeof(head)); int u=pre[vs]=vs,flow=0,aug=INF; gap[0]=NV; while(level[vs]<NV){ bool flag=false; for(int &i=cur[u]; i!=-1; i=edge[i].next){ int v=edge[i].v; if(edge[i].cap!=edge[i].flow && level[u]==level[v]+1){ flag=true; pre[v]=u; u=v; //aug=(aug==-1?edge[i].cap:min(aug,edge[i].cap)); aug=min(aug,edge[i].cap-edge[i].flow); if(v==vt){ flow+=aug; for(u=pre[v]; v!=vs; v=u,u=pre[u]){ edge[cur[u]].flow+=aug; edge[cur[u]^1].flow-=aug; } //aug=-1; aug=INF; } break; } } if(flag) continue; int minlevel=NV; for(int i=head[u]; i!=-1; i=edge[i].next){ int v=edge[i].v; if(edge[i].cap!=edge[i].flow && level[v]<minlevel){ minlevel=level[v]; cur[u]=i; } } if(--gap[level[u]]==0) break; level[u]=minlevel+1; gap[level[u]]++; u=pre[u]; } return flow; } int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; int main(){ int n,m,k,a,b,map[55][55]; bool flag[55][55]; while(~scanf("%d%d%d",&n,&m,&k)){ int tot=0; for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ scanf("%d",&map[i][j]); tot+=map[i][j]; } } memset(flag,0,sizeof(flag)); while(k--){ scanf("%d%d",&a,&b); --a; --b; flag[a][b]=1; } vs=n*m; vt=vs+1; NV=vt+1; NE=0; memset(head,-1,sizeof(head)); for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ if(i+j&1){ if(flag[i][j]) addEdge(vs,i*m+j,INF); addEdge(vs,i*m+j,map[i][j]); for(int k=0; k<4; ++k){ int nx=i+dx[k],ny=j+dy[k]; if(nx<0 || nx>=n || ny<0 || ny>=m) continue; addEdge(i*m+j,nx*m+ny,2*(map[i][j]&map[nx][ny])); } }else{ if(flag[i][j]) addEdge(i*m+j,vt,INF); addEdge(i*m+j,vt,map[i][j]); } } } printf("%d\n",tot-ISAP()); } return 0; }
相关文章推荐
- 不允许用大于号小于号,比较任意两个数字大小
- PSP记录个人项目耗时情况
- iOS instancetype与id的比较
- 3月23日学习所得
- Sass学习笔记
- 使用热敏小票打印机批量打印一维条码的可行性
- 视频云直播:场景、技术及优化
- C++函数后加const详解
- 第四周项目五用递归方法求解
- 重学STM32----(二)
- 第三次作业2
- 【多线程】--生产者消费者模式--synchronized版本
- JS 中的五个假值
- 代码复审
- mybatis 时间类型比较
- nginx如何做到TCP的负载均衡
- inline函数出现无法解析的错误
- iOS import,include和class的区别
- 基本数据类型的封装类
- 【递推公式】HDU1143Tri Tiling