HDU 1565 - 方格取数(1) - [状压DP][网络流 - 最大点权独立集和最小点权覆盖集]
题目链接:https://cn.vjudge.net/problem/HDU-1565
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
看了一下题目,立刻就想起来曾经好像在网络流专题里做过类似题目,
一翻博客,果然找到了这题的brother:http://www.cnblogs.com/dilthey/p/7401563.html
题解:
解法①:
既然,连这题的加强版 HDU 1569 - 方格取数 都可以用网络流做,这题当然也可以,代码几乎就是一点点修改:
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define MAX 405
#define INF 0x3f3f3f3f
#define id(i,j) (i-1)*n+j
using namespace std;
struct Edge{
int u,v,c,f;
};
struct Dinic
{
int s,t;
vector<Edge> E;
vector<int> G[MAX];
bool vis[MAX];
int lev[MAX];
int cur[MAX];
void init(int l,int r)
{
E.clear();
for(int i=l;i<=r;i++) G[i].clear();
}
void addedge(int from,int to,int cap)
{
E.push_back((Edge){from,to,cap,});
E.push_back((Edge){to,from,,});
int m=E.size();
G[from].push_back(m-);
G[to].push_back(m-);
}
bool bfs()
{
memset(vis,,sizeof(vis));
queue<int> q;
q.push(s);
lev[s]=;
vis[s]=;
while(!q.empty())
{
int now=q.front(); q.pop();
for(int i=,_size=G[now].size();i<_size;i++)
{
Edge edge=E[G[now][i]];
int nex=edge.v;
if(!vis[nex] && edge.c>edge.f)
{
lev[nex]=lev[now]+;
q.push(nex);
vis[nex]=;
}
}
}
return vis[t];
}
int dfs(int now,int aug)
{
if(now==t || aug==) return aug;
int flow=,f;
for(int& i=cur[now],_size=G[now].size();i<_size;i++)
{
Edge& edge=E[G[now][i]];
int nex=edge.v;
if(lev[now]+ == lev[nex] && (f=dfs(nex,min(aug,edge.c-edge.f)))>)
{
edge.f+=f;
E[G[now][i]^].f-=f;
flow+=f;
aug-=f;
if(!aug) break;
}
}
return flow;
}
int maxflow()
{
int flow=;
while(bfs())
{
memset(cur,,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
}dinic;
int n,grid[][],sum;
int d[][]={{,+},{+,},{,-},{-,}};
bool inmap(int i,int j){return( <=i && i<=n && <=j && j<=n );}
int main()
{
while(scanf("%d",&n)!=EOF)
{
dinic.init(,n*n+);
dinic.s=, dinic.t=n*n+;
sum=;
for(int i=;i<=n;i++)
{
for(int j=;j<=n;j++)
{
scanf("%d",&grid[i][j]);
sum+=grid[i][j];
if((i+j)%)
{
for(int k=;k<;k++) if(inmap(i+d[k][],j+d[k][])) dinic.addedge(id(i,j), id(i+d[k][],j+d[k][]), INF);
dinic.addedge(dinic.s, id(i,j), grid[i][j]);
}
else dinic.addedge(id(i,j), dinic.t, grid[i][j]);
}
}
printf("%d\n",sum-dinic.maxflow());
}
}
解法②:
当然,既然放在状压DP专题,当然也说明了这种题目,在列数在比较小的情况下,可以用状压DP做(HDU1569因为列数达到50,所以不行);
首先,由于1<<20的状态数,还是偏大了点,依然选择类似于http://www.cnblogs.com/dilthey/p/7604432.html中筛选状态的操作,得到最多的状态数为17711;
然后,设dp[r][i]:表示第r行为状态i时(显然这个i在二进制表示下,每一位上,0表示不取对应位置的数,1表示取),从第一行到第r行能取到的最大的和;
而要得到dp[r][i],只需要把dp[r-1][j]枚举一遍,做相应的一定的计算,再保证维护dp[r][i]始终为最大即可,这就是状态转移;
其中所谓“做相应的一定的计算”,需要一个calc(r, state)函数:
calc(r, state)返回值为:第r行当状态为state时,取到的数之和;
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,grid[][],ans;
int dp[][];
int state[],state_cnt;
int calc(int r,int state)
{
int ans=;
int cnt=n;
while(state)
{
if(state&) ans+=grid[r][cnt];
state=state>>;
cnt--;
}
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(dp,,sizeof(dp));
for(int i=;i<=n;i++) for(int j=;j<=n;j++) scanf("%d",&grid[i][j]);
state_cnt=;
for(int i=;i<(<<n);i++)
{
if(i&(i<<)) continue;
dp[][state_cnt]=calc(,i);
state[state_cnt++]=i;
}
for(int r=;r<=n;r++)
{
for(int i=;i<state_cnt;i++)//枚举第r行状态
{
for(int j=;j<state_cnt;j++)//枚举第r-1行状态
{
if(state[i]&state[j]) continue;
dp[r][i]=max(dp[r][i],dp[r-][j]+calc(r,state[i]));
}
}
} ans=;
for(int i=;i<state_cnt;i++) ans=max(ans,dp[n][i]);
printf("%d\n",ans);
}
}
PS.本题其实和POJ 1185:http://www.cnblogs.com/dilthey/p/7604432.html比较像,做法思路一脉相承,会做POJ1185的话,做这题应该不难。
HDU 1565 - 方格取数(1) - [状压DP][网络流 - 最大点权独立集和最小点权覆盖集]的更多相关文章
- hdu 2167 方格取数 【状压dp】(经典)
<题目链接> 题目大意: 给出一些数字组成的n*n阶矩阵,这些数字都在[10,99]内,并且这个矩阵的 3<=n<=15,从这个矩阵中随机取出一些数字,在取完某个数字后,该数 ...
- hdu 1565 方格取数(1) 状态压缩dp
方格取数(1) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- HDU1565 方格取数(1) —— 状压DP or 插头DP(轮廓线更新) or 二分图点带权最大独立集(最小割最大流)
题目链接:https://vjudge.net/problem/HDU-1565 方格取数(1) Time Limit: 10000/5000 MS (Java/Others) Memory L ...
- 网络流(最大流) HDU 1565 方格取数(1) HDU 1569 方格取数(2)
HDU 1565 方格取数(1) 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的 ...
- HDU 1565 方格取数(1) 轮廓线dp
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1565 方格取数(1) Time Limit: 10000/5000 MS (Java/Others) ...
- HDU 1565 方格取数 状压dp
题目: 给你一个n*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大. Input 包括多 ...
- hdu 1565 方格取数(1)(状态压缩dp)
方格取数(1) Time Limit: 10000/5000 MS (J ...
- HDU 1565 方格取数(1)(最大点权独立集)
http://acm.hdu.edu.cn/showproblem.php?pid=1565 题意: 给你一个n*n的格子的棋盘,每个格子里面有一个非负数. 从中取出若干个数,使得任意的两个数所在的格 ...
- HDU 1565 方格取数(1) ——插头DP
[题目分析] 其实直接状压就可以了. 但是有点闲,又写了一个可读性极差,智商低下,很(gou)好(pi)的代码 [代码] #include <cstdio> #include <cs ...
随机推荐
- 泛泰A870K去掉相机快门声音的方法
首先ROOT手机,挂载读写,/system/media/audio/ui里面哈,把camera-click.ogg改成camera-click.ogg.bak就可以了 转载自:http://bbs.9 ...
- 《倾国倾城》全套源代码:client+服务端+资源,歧视复制帖子
郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠,支持郝萌主,捐赠数额任意.重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 游戏官方下 ...
- js节流函数高级版
节流函数其主要作用就是防止用户在短时间内多次触发该事件. <!DOCTYPE html> <html lang="en"> <head> < ...
- LHC大神问的矩阵转置问题
数学中线性代数中提到的矩阵转置,其实在我们的业务场景中也有需要的地方,比如LHC大神问到的这个问题 那么如何进行行列转换呢? 代码如下: <?php $array=array( '部门1'=&g ...
- 统计js数组中奇数元素的个数
如何统计一个JS数组中奇数元素的个数呢? 这是群友提出的一个问题,大部分群友给出的是遍历 然后对2取模,得到最终结果. 这样的写法是最容易想得到的,那么有没有其他思路呢? 这里我提供另外一种思路,我们 ...
- openssl & openssh
什么是OpenSSL众多的密码算法.公钥基础设施标准以及SSL协议,或许这些有趣的功能会让你产生实现所有这些算法和标准的想法.果真如此,在对你表示敬佩的同时,还是忍不住提醒你:这是一个令人望而生畏的过 ...
- RESTFul basic introduction
http://www.ruanyifeng.com/blog/2011/09/restful.html
- 【面试题】Java EE面试题
第一部分:Servlet/JSP快速入门 1. 请列举至少6种javaEE技术,并简述其作用. 解析: javaEE是一系列的技术,主要包扩13种.对于开发人员来说,了解几种主要的技术是非常 ...
- 怎样更改SQL Server 2008的身份验证方式
大家都知道sql server 有两种登录验证方式,即sql server验证方式和windows验证方式,但是sql server默认的是windows登录验证方式,我们如何启用sql server ...
- PHP魔术变量和魔术方法
基础知识:魔术变量和魔术方法 魔术变量:最初PHP魔术变量的出现主要是为了方便开发者调试PHP的代码;当然也可以利用这个实现特殊需求.在写法上魔术变量前后都有两个下划线. 如:_LINE_:返回文件中 ...