HUST算法实践__POJ1636

本文介绍了一种解决监狱囚犯交换问题的算法,该问题涉及到保持某些危险囚犯不在同一监狱。通过构建无向图并进行染色,将问题转化为01背包问题,使用动态规划求解最大交换人数。算法首先进行图的深度优先搜索染色,然后将连通分支转化为物品,最后利用动态规划找到最大交换数量。
摘要由CSDN通过智能技术生成

问题描述

In order to lower the risk of riots and escape attempts, the boards of two nearby prisons of equal prisoner capacity, have decided to rearrange their prisoners among themselves. They want to exchange half of the prisoners of one prison, for half of the prisoners of the other. However, from the archived information of the prisoners’ crime history, they know that some pairs of prisoners are dangerous to keep in the same prison, and that is why they are separated today, i.e. for every such pair of prisoners, one prisoners serves time in the first prison, and the other in the second one. The boards agree on the importance of keeping these pairs split between the prisons, which makes their rearrangement task a bit tricky. In fact, they soon find out that sometimes it is impossible to fulfil their wish of swapping half of the prisoners. Whenever this is the case, they have to settle for exchanging as close to one half of the prisoners as possible.

输入

On the first line of the input is a single positive integer n, telling the number of test scenarios to follow. Each scenario begins with a line containing two non-negative integers m and r, 1 < m < 200 being the number of prisoners in each of the two prisons, and r the number of dangerous pairs among the prisoners. Then follow r lines each containing a pair xi yi of integers in the range 1 to m,which means that prisoner xi of the first prison must not be placed in the same prison as prisoner yi of the second prison.

输出

For each test scenario, output one line containing the largest integer k <= m/2 , such that it is possible to exchange k prisoners of the first prison for k prisoners of the second prison without getting two prisoners of any dangerous pair in the same prison.

算法思想

根据题目的描述我们可以知道, 假如一号监狱中的i号犯人与二号监狱中的j号犯人之间存在危险, 那么在将i号犯人从一号监狱移动到的二号监狱的同时, 必须也将j号犯人移动到一号监狱. 这意味着, 我们可以将i号犯人和j号犯人的移动看作同时发生的.

更一般地, 一号监狱中的某个犯人, 可能与多个二号监狱中的犯人存在危险, 而这些二号监狱中的犯人中也可能不止与一号监狱中的那个犯人存在危险.

所以, 我们可以把犯人之间的关系抽象为一个无向图, 若两个犯人之间存在危险, 则有一条无向边将两个犯人相连. 我们借助深度优先搜索, 可以很方便地将图染色, 求出所有的连通分支.

我们将每个连通分支看作一个物品. 所有连通分支保存在数组w[][2]中, w[i][0]表示第i个连通分支中来自一号监狱的人数, w[i][1]表示第i个连通分支中来自二号监狱的人数.

此时, 这个看似生疏的监狱分配问题, 已经可以转化为我们熟悉的01背包问题: 背包有两个口袋, 每个口袋的容量为m/2, 我们要在保证两个口袋重量一致的前提下, 尽可能地选择总重量最大的物品.

代码实现
#include<iostream>
#include<cstring> 
#include<algorithm>
#include<vector>
using namespace std;

vector<int>graph[401];
bool vis[401];
int color[401];		//染色
int cnt_color;			//颜色 
int dp[101][101]; 
int w[401][2];			//物品 
int n,m=0,r;
int a,b;
bool flag;

void init(){		//初始化 
	flag=0;
	for(int i=1;i<=2*m;i++) graph[i].clear();
	memset(vis,0,sizeof(vis));
	memset(color,0,sizeof(color));
	memset(dp,1<<7,sizeof(dp));		//条件限制 
	dp[0][0]=0;
	memset(w,0,sizeof(w)); 
	cnt_color=1;
}
void dfs(int s,int cnt){		//图染色 
	vis[s]=1;
	color[s]=cnt;
	for(int i=0;i<graph[s].size();i++){
		int to=graph[s][i];
		if(vis[to]) continue;
		dfs(to,cnt);
	}
}
void transform(){			//将连通分支转化为物品 
	for(int i=1;i<=m;i++){
		w[color[i]][0]++;
	}
	for(int i=m+1;i<=2*m;i++){
		w[color[i]][1]++;
	}
}
void solve(){				//动态规划 
	for(int i=1;i<cnt_color;i++){
		int w1=w[i][0],w2=w[i][1];
		for(int j=m/2;j>=w1;j--){
			for(int k=m/2;k>=w2;k--){
				dp[j][k]=max(dp[j][k],dp[j-w1][k-w2]+w1+w2);
			}
		}
	}
}

int main(){
	cin>>n;
	while(n--){
		cin>>m>>r;
		init();
		for(int i=0;i<r;i++){
			cin>>a>>b;
			b+=m;
			graph[a].push_back(b);		//双向建图 
			graph[b].push_back(a);
		}
		for(int i=1;i<=2*m;i++){		//染色 
			if(vis[i]) continue;
			dfs(i,cnt_color);
			cnt_color++;
		} 
		transform();
		solve();
		for(int i=m/2;i>0;i--){
			if(dp[i][i]>=0){
				cout<<dp[i][i]/2<<'\n';
				flag=1;
				break;
			}
		}
		if(flag) continue;
		cout<<0<<'\n';
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值