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); } } }