【codevs1907】【方格取数3】二分图最大带权独立集
2017-10-31 19:09
513 查看
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=59001242
向大(hei)佬(e)势力学(di)习(tou)
问题描述:
在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任
意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
«编程任务:
对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入描述 Input Description
第1 行有2 个正整数m和n,分别表示棋盘的行数
和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。
输出描述 Output Description
将取数的最大总和输出
样例输入 Sample Input
3 3
1 2 3
3 2 3
2 3 1
样例输出 Sample Output
11
数据范围及提示 Data Size & Hint
n,m<=30
最近学图论,精神大餐吃的太饱不消化,就多攻克一下网络流和二分图吧。
这道题其实是最小割的应用,兼有二分图的思想在里面。想想,要使选出来的最大,就要使留下来的最小,而我们刚好有求最小的东西(最大流=最小割)
方格是天然的二分图,可以将方格染成交叉的黑白图,黑的丢一边,白的丢一边,相邻的连边,容量正无穷,表示不能割掉。源点与黑的连边,容量即为格子中的数,白点与汇点连边,同理。割掉的边即为舍弃的格子。
代码(注意点的编号不能重复)
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int oo=0x3f3f3f3f; int m,n,a[50][50],sz=0,top; struct Node{ int to,nxt,flow,cap; }r[6000]; int head[3000],hh=1,dep[3000]; queue<int> q; void adde(int fr,int to,int val){ hh++; r[hh].to=to; r[hh].flow=0; r[hh].cap=val; r[hh].nxt=head[fr]; head[fr]=hh; hh++; r[hh].to=fr; r[hh].flow=0; r[hh].cap=0; r[hh].nxt=head[to]; head[to]=hh; } bool bfs(){ while(!q.empty()) q.pop(); memset(dep,-1,sizeof(dep)); q.push(0); dep[0]=0; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=r[i].nxt){ int v=r[i].to; if(dep[v]==-1&&r[i].cap-r[i].flow>0){ dep[v]=dep[u]+1; q.push(v); } } if(u==top) return true; } return false; } int dfs(int u,int delta){ if(u==top||delta==0) return delta; int rt=0,f; for(int i=head[u];i;i=r[i].nxt){ int v=r[i].to; if(dep[v]==dep[u]+1&&r[i].cap-r[i].flow>0){ f=dfs(v,min(delta,r[i].cap-r[i].flow)); rt+=f; delta-=f; r[i].flow+=f; r[i^1].flow-=f; } if(delta==0) break; } return rt; } int main(){ top=2500; int tot=0; scanf("%d%d",&m,&n); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++){ scanf("%d",&a[i][j]); tot+=a[i][j]; if((i+j)%2==0){ adde(0,i*50+j,a[i][j]); if(i-1>0) adde(i*50+j,(i-1)*50+j,oo); if(i+1<=m) adde(i*50+j,(i+1)*50+j,oo); if(j-1>0) adde(i*50+j,i*50+j-1,oo); if(j+1<=n) adde(i*50+j,i*50+j+1,oo); } if((i+j)%2!=0){ adde(i*50+j,top,a[i][j]); } } int ans=0; while(bfs()){ ans+=dfs(0,oo); } printf("%d",tot-ans); return 0; }
总结:
1、遇到求最值问题,可从最大、最小两个方向出发。
2、转化、建边也是很需要思维的东西
3、天然的二分图:方格。当一个东西有两个元素时,可考虑二分图或网络流(源点汇点各一方嘛)
相关文章推荐
- 【codevs1907】方格取数3 最大点权独立集
- codevs1907方格取数最大流
- ★ 方格取数3 二分图的最大点权独立集
- codevs 1907:方格取数3
- codevs1907 方格取数 3||tyvj1338QQ农场|网络流
- 【Codevs1907】方格取数3(最小割)
- HDU 1565 方格取数(1) 二分图最大点权独立集
- hdu1569 方格取数(2) 二分图最大点权独立集
- LOJ6007 「网络流 24 题 - 8」 方格取数 二分图最大点权独立集
- codevs1922(二分图最大独立集)
- codevs1022覆盖(二分图最大匹配)
- [网络流24题][codevs1922] 骑士共存问题 二分图最大独立集
- CodeVS1907 方格取数3
- 【codevs1907】[网络流24题]方格取数3
- 【BZOJ2744】【codevs2366】朋友圈,二分图最大匹配
- [codevs1907]方格取数3 最小割
- CodeVS 1907 方格取数 3 (ISAP)
- Codevs 1227方格取数 (费用流
- codevs 1922 骑士共存问题||二分图||最大独立集||二分图匹配||Dinic与匈牙利算法的讨论||网络流
- 【codevs1907】方格取数3(最大流最小割定理)