poj 1636 Prison rearrangement (监狱调整)

来源:互联网 发布:企业淘宝认证资料 编辑:程序博客网 时间:2024/04/29 20:10

  感想:   虽然本人也是看别人代码写出来的,但对于我这样的dp菜鸟,实在太有收获了,希望大家无论自己悟出来还是看别人的博客,都争取自己写出来,这题确实对练习dp的人太有益处了。


 思路:   一开始就得弄清楚,1号房和2号房是互相联系的,有关系的,他们之间构成一个连通分量(这个必须得弄清楚,不然

后面的多重背包完全会想不通的),只要选取其中连通分量中的一个,其余的都要进行交换操作


在开始输入完毕后就应该 深搜其连通分量,最坏的情况也就是这个点单独构成一个连通分量,相当于他的交换与外界无关系(此处需要注意的是,如果按照1号牢房来进行搜索,可能2号牢房中还有那些与外界无关系的牢友没有被考虑到,所以搜索得进行2次)。


dp[ i ][ j ]表示的是1号牢房交换i个人,2号牢房交换j个人。(dp数组是个bool数组,只需要判断其是否存在)。

最后的判断就变成判断dp[ i ][ i ]是否存在。


连通分量分好了之后,这就转化成一个01背包问题,只是这个01背包只是判断


状态转换方程:dp[k][i][j] = dp[k-1][i-a[k]][j-b[k]] || dp[k-1][i][j]。简单解释一下:dp[k][i][j]表示对前K组,用监狱A的i个人和监狱B的j个人交换是否成功。前K组的解与前K-1组有关。当前K-1组解决后,只要加上第K组就可以搞定前K组。对第K组有两种选择:选或不选。


慢慢看代码吧。

#include<cstdio>#include<cstring>#include<iostream>using namespace std;int r,m,asize,bsize;const int size = 205;bool vis[2][size],dp[size][size];/*//visited[0][i] 表示用1号牢房中的点i是否被访问过  //visited[1][i] 表示用2号牢房中的点i是否被访问过*/int map[size][size];void DFS(int side,int id)//side 分别为0,1表示此时访问的是哪个牢房,id就是这个牢房中牢友的编号{   vis[side][id] = true;   if(side == 0)   {    asize++;    for(int i=1;i<=m;++i)    {      if(map[id][i]&&!vis[1][i])//访问了的话就不访问了      {        DFS(1,i);      }    }   }   else   {       bsize++;       for(int i=1;i<=m;++i)       {          if(map[i][id]&&!vis[0][i])            DFS(0,i);       }   }}void kpack()//01背包的函数{  dp[0][0] = true;  for(int i=m/2;i>=asize;--i)   for(int j=m/2;j>=bsize;--j)   {     if(dp[i][j]||dp[i-asize][j-bsize]) dp[i][j] = true;   }}int main(){   int T;   cin>>T;   while(T--)   {    memset(map,0,sizeof(map));//初始化得放在循环中,我就因为这个调了半天    memset(vis,false,sizeof(vis));    memset(dp,false,sizeof(dp));    cin>>m>>r;    for(int i=1;i<=r;++i)     {         int a,b;         cin>>a>>b;         map[a][b] = 1;     }     for(int i=1;i<=m;++i)//从左边开始探究  这里是对那些组成连通分量的进行dp,     {      if(vis[0][i]) continue;      asize = 0;      bsize = 0;      DFS(0,i);      kpack();     }     for(int i=1;i<=m;++i)//怕有剩下的右边的边,对右边的边进行连通分量进行dp     {         if(vis[1][i])  continue;         asize = 0;         bsize = 0;         DFS(1,i);         kpack();     }     for(int i=m/2;i>=0;i--)     if(dp[i][i])      {          cout<<i<<endl;          break;      }   }  return 0;}


原创粉丝点击