菜鸟学算法之POJ1636 Prison Rearrangement
来源:互联网 发布:mac快捷键缩小窗口 编辑:程序博客网 时间:2024/05/16 10:01
第一次看到这个题的时候,真的是一筹莫展,完全找不到思路。百度“POJ1636”,发现好多人使用DP方法,但是我还是不明白,这道题怎么就用DP方法呢?于是开始看大神们的解题思路,大致明白了一些,但是仍然是糊涂的,毕竟是别人的思路!于是我开始自己的思考。
第一步,通过简单的画图,发现这个问题的特点有哪些。
我们再来考虑复杂的情况:
这样我们终于摸到了问题的本质:每一个连通分量必须整体交换!
至于如何寻找所有的连通分量,可以使用并查集,或者DFS。对于某一个连通分量i ,i左边的人数为a[i],i右边的人数为b[i],这样就形成了2个数组,a[] 和 b[] ,在这两个数组中,选取若干对数,使得 SUM( a[i] ) = SUM( b[i] ) ,且SUM( a[i] ) 最大(当然肯定小于或等于 m/2 啦 ),就是我们的目标!
为了达成这个目标,我们设计DP函数,dp[i][j]:如果左边i个人与右边j个人可以交换,则dp[i][j]=true,否则dp[i][j]=false
下面是AC的代码,有详细的注释,力争清晰易懂。
/** 使用DFS寻找连通分量* 在第一版的代码中,我只是将相互关联的人作为一个连通子集,结果,WA了!* 在第二版的代码中,将每一个孤立点也作为一个连通子集,这样才AC。* 188K 63 MS*/#include <iostream>#include<string.h>#include<stdio.h>using namespace std;int const MAXM = 202; // the max value of mint num_case , m , r ; // just as the problem describesint groupNum; // 组号bool visited[2*MAXM]; // 是否已经被访问过bool G[MAXM][MAXM]; // Graghint Left[2*MAXM] , Right[2*MAXM]; // 每个连通子集左边的人数和右边的人数bool dp[MAXM/2][MAXM/2]; // DP[i][j]=true表示左边i个人与右边j个人交换能够成功,否则不行// DFS:搜索连通分量 ,注意,孤立点作为一个特殊的连通分量void dfs(int a){ // termination condition if( true == visited[a] ) return; visited[a] = true; if( a <= m ) // 左边的人 Left[ groupNum ] ++; else // 右边的人 Right[ groupNum ] ++; // search for its neighbors if( a <= m ) // 这个人在左边 { for(int i=m+1;i<=2*m;i++) if( true == G[a][i-m] && false == visited[i] ) dfs( i ); } else // 这个人在右边 { for(int i=1;i<=m;i++) { if( true == G[i][a-m] && false == visited[i] ) dfs( i ); } }}int main(){ scanf("%d",&num_case); while( num_case -- ) { int i , j , k; // Initialization memset( G , 0 , sizeof(G) ); memset( visited , 0 , sizeof(visited) ); memset( Left , 0 , sizeof(Left) ); memset( Right , 0 , sizeof(Right) ); memset( dp , 0 , sizeof(dp) );// memset( Left_visited , 0 , sizeof(Left_visited) ); groupNum = 1;<span style="white-space:pre"></span>// 组号,从1开始计数 // input scanf("%d%d",&m , &r ); int t1 , t2 ; for(i=0;i<r;i++) { scanf("%d%d",&t1 , &t2 ); G[t1][t2] = true; } // process: 遍历所有的人 for(i=1;i<=2*m;i++) if( false == visited[i] ) // 没有被访问 { dfs(i); groupNum ++; } // 开始DP groupNum --; // 此时的groupNum就是连通子集的数目 // 如何理解这个DP?我们考察dp[i][j]能否达到,并考察第k个连通分量的人是否要交换 // 如果不交换第i个连通分量,那么dp[i][j] = dp[i][j] // 如果交换,那么 dp[i][j] = dp[ i-Left[k] ][ j-Right[k] ] dp[0][0] = true; // 初始化DP for(k=1;k<=groupNum;k++) for(i=m/2;i>=Left[k];i--) for(j=m/2;j>=Right[k];j--) { if( true == dp[ i - Left[k] ][ j - Right[k] ] ) dp[i][j] = true; } int result = m/2 ; while( false == dp[result][result] ) // dp[i][i] = true , 找到最大的i result --; printf("%d\n",result); } return 0;}
0 0
- 菜鸟学算法之POJ1636 Prison Rearrangement
- 监狱调整 POJ1636 Prison rearrangement 传递闭包FLoyd DFS 二维背包 动态规划DP
- pku1636 Prison rearrangement
- POJ 1636 Prison rearrangement
- poj 1636 Prison rearrangement
- poj 1636 Prison rearrangement
- POJ 1636 Prison rearrangement
- POJ 1636 Prison rearrangement
- POJ 1636 Prison rearrangement
- poj 1636 Prison rearrangement
- POJ 1636 Prison rearrangement
- 【poj 1636】Prison rearrangement dp
- POJ 1636 Prison rearrangement 笔记
- 菜鸟学算法之 POJ1011 Sticks
- 菜鸟学算法之动态规划01
- poj 1636 Prison rearrangement (监狱调整)
- (中等) 背包 HOJ 1293 Prison Rearrangement
- poj 1634 Prison rearrangement
- mysql笔记02:source命令导入大数据速度慢优化
- 《信息系统开发与管理之DFD》
- autohotkey实现自动totalcmd 8.52点击未激活提示窗口
- 假设表达式中允许包括3中括号:(,[,{,设计一个算法采用顺序栈判断表达式中的括号是否正确配对
- 关于JDK在windows环境下的安装与环境变量配置的问题
- 菜鸟学算法之POJ1636 Prison Rearrangement
- 第十九篇:学习好C++需要的读的书籍
- 2D Conforming Triangulations
- Ajax异步加载问题
- Oracle pl/sql编程 2---plsql变量与常量
- Linux 线程 动画
- 快速删除docker中的容器
- 非递归快速排序
- 巴什博奕