POJ1636

题目描述

两个人数一样的监狱,有一些囚犯不能在一起,两个监狱要等数量( < m/2)交换一些囚犯,问最多可以交换多少个囚犯;

题目思路

1. 将不能在一起的囚犯看作是两个有关系的点,求这个图中有多少个块(连通分量),
每个块中 在1号监狱的人数,在2号监狱的人数 记录下来

2. 想要监狱人数相等
从1号监狱出去多少人就要进来多少人。 求最大出去多少人
上一步求出了 n个块 (a监狱人数, b监狱人数) 每个块至少这样交换。

这样考虑 有n个块,存在的情况数是 i号连通图,交换 、不交换;
所有可能出现情况列出来,最后寻找交换的a监狱等于b监狱人数就是符合要求的
在列出来符合要求找出最大的人数

有点像01背包 我们假设状态转移方程
dp[i][j] 表示从a监狱出去i个人和从b监狱出去j个人的情况下是否可行

dp[0][0] = 1;

套用背包问题
for (int i = 0; i < group; i++) {
for (int j = n/2; j >= aa[i] ; j--) {
for (int k = n/2; k >= bb[i] ; k--) {
if(dp[j-aa[i]][k-bb[i]] == 1) dp[j][k] = 1;
}
}
}

题目代码

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.StringTokenizer;

public class Main {
    private static ArrayList<Integer>[] arrayLists;
    private static boolean[] visited;
    private static int n, r, aSize, bSize;

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int T = Integer.valueOf(st.nextToken());

        for (int t = 1; t <= T; t++) {
            st = new StringTokenizer(br.readLine());
            n = Integer.valueOf(st.nextToken()); // 一个监狱的人数
            r = Integer.valueOf(st.nextToken()); // 囚犯的危险关系数
            // 将2号监狱囚犯的编号+n,这样可以看作不重复的
            arrayLists = new ArrayList[n*2+1];
            visited = new boolean[n*2+1];
            for (int i = 0; i < n*2+1; i++) {
                arrayLists[i] = new ArrayList<Integer>();
            }
            for (int i = 0; i < r; i++) {
                st = new StringTokenizer(br.readLine());
                int a = Integer.valueOf(st.nextToken());
                int b = Integer.valueOf(st.nextToken()) + n;
                arrayLists[a].add(b);
                arrayLists[b].add(a);
            }

            //1. 求dfs 连通图的区块个数以及
            int aa[] = new int[n*2];
            int bb[] = new int[n*2];
            int group = 0;
            for (int i = 1; i < n*2+1; i++) {
                if(!visited[i]) {
                    aSize = 0;
                    bSize = 0;
                    dfs(i);
                    aa[group] = aSize;
                    bb[group++] = bSize;
                }
            }

            // 从第一个监狱出来i个人和从第二个监狱出来j个人进行替换
            int[][] dp = new int[(n/2)+1][(n/2)+1];
//            int[][] dp = new int[1000][1000];
            dp[0][0] = 1;
            // 二维0/1背包
            for (int i = 0; i < group; i++) {
                for (int j = n/2; j >= aa[i] ; j--) {
                    for (int k = n/2; k >= bb[i] ; k--) {
                        if(dp[j-aa[i]][k-bb[i]] == 1) dp[j][k] = 1;
                    }
                }
            }

            for (int i = n / 2; i >= 0; i--) {
                if(dp[i][i] == 1) { // 保证两个监狱人数相等
                    System.out.println(i);
                    break;
                }
            }
        }
    }

    private static void dfs(int cur) {
        visited[cur] = true;

        if(cur <= n) aSize++;
        else bSize++;

        for(int i = 0; i < arrayLists[cur].size(); i++) {
            int next = arrayLists[cur].get(i);
            if(visited[next]) continue;
            dfs(next);
        }
    }
}

 

posted @ 2021-07-16 17:02  姓蜀名黍  阅读(199)  评论(0编辑  收藏  举报