POJ1636 动态规划+并查集

 POJ1636

问题重述:

两个监狱中各有m个囚犯,欲对这两个监狱中的囚犯进行等数量的交换。已知某些囚犯不能关押在同一个监狱,求解可以交换人数的最大值k (k < m/2)。

 

分析:

假设监狱1中的囚犯a与监狱2中的囚犯b不能共存。那么假如对a进行交换,也必须对b进行交换。因此,根据互斥关系建立的连通集两边的成员必须同时进行交换。

 

求解步骤:

1)  根据已知的互斥关系,采用并查集建立连通集,分别记录每个连通集在两个监狱中的成员数目,记为v1, v2。

2)  采用动态规划算法,用布尔变量dp[i][j]表示监狱1中i个囚犯与监狱2中的j个囚犯进行交换的可行性。则有dp[i][j] = dp[i – v1[k]][j – v2[k]]

3)  满足dp[i][i] = 1, i < m/2的i的最大值即所求的解。

  1 //Memory: 580K        Time: 63MS

  2 #include <iostream>

  3 #include <cstring>

  4 #include <cstdio>

  5 

  6 using namespace std;

  7 

  8 const int maxn = 410;

  9 int m, r;

 10 bool g[maxn][maxn];

 11 int f[maxn];

 12 int nl[maxn];

 13 int nr[maxn];

 14 bool vis[maxn];

 15 int v1[maxn], v2[maxn];

 16 int cnt;

 17 bool dp[maxn][maxn];

 18 

 19 void makeset()

 20 {

 21     memset(f, 0, sizeof(f));

 22     memset(nl, 0, sizeof(nl));

 23     memset(nr, 0, sizeof(nr));

 24     for (int i = 1; i <= 2 * m; i++)

 25         f[i] = i;

 26     for (int i = 1; i <= m; i++) {

 27         nl[i] = 1;

 28         nr[i] = 0;

 29     }

 30     for (int i = 1 + m; i <= m * 2; i++) {

 31         nr[i] = 1;

 32         nl[i] = 0;

 33     }

 34 }

 35 

 36 int find(int a) {

 37     if (f[a] == a) return a;

 38     f[a] = find(f[a]);

 39     return f[a];

 40 }

 41 

 42 void uni(int a, int b) {

 43     int sa = find(a);

 44     int sb = find(b);

 45     if (sa != sb) {

 46         f[sa] = sb;

 47         nl[sb] += nl[sa];

 48         nr[sb] += nr[sa];

 49     }

 50 }

 51 

 52 void init()

 53 {

 54     makeset();

 55     for (int i = 1; i <= m; i++) {

 56         for (int j = m + 1; j <= m * 2; j++) if (g[i][j]) {

 57             uni(i, j);

 58         }

 59     }

 60     cnt = 0;

 61     for (int i = 1; i <= m * 2; i++) {

 62         int s = find(i);

 63         if (s == i) {

 64             v1[cnt] = nl[s];

 65             v2[cnt++] = nr[s];

 66         }

 67     }

 68 }

 69 

 70 int main()

 71 {

 72     int cas;

 73     cin >> cas;

 74     while (cas--) {

 75         memset(g, 0, sizeof(g));

 76         scanf("%d%d", &m, &r);

 77         int a, b;

 78         for (int i = 0; i < r; i++) {

 79             scanf("%d%d", &a, &b);

 80             g[a][b + m] = 1;

 81         }

 82         init();

 83 

 84         memset(dp, 0, sizeof(dp));

 85         dp[0][0] = 1;

 86         for (int i = 0; i < cnt; i++) {

 87             for (int j = m/2; j >= 0; j--)  ////此处必须进行倒序循环:每次循环的dp都由上一轮循环后序号较小的dp确定,倒序循环避免提前更新序号较小的dp

 88                 for (int k = m/2; k >= 0; k--) {  //同上

 89                     if (dp[j][k] && j + v1[i] <= m/2 && k + v2[i] <= m/2)

 90                         dp[j + v1[i]][k + v2[i]] = 1;

 91                 }

 92         }

 93         

 94         for (int i = m / 2; i >= 0; i--) {

 95             if (dp[i][i]) {

 96                 cout << i <<endl;

 97                 break;

 98             }

 99         }

100     }

101     return 0;

102 }

你可能感兴趣的:(动态规划)