【poj 1636】Prison rearrangement dfs+01背包

本文介绍了一种解决特定类型图论问题的方法,通过将问题转换为二维01背包问题来求解。针对n个人之间的m组关系约束,采用深度优先搜索(DFS)和动态规划(DP)技术进行节点打包和状态转移,最终找到A和B两组监狱分配人员的最大数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:首先由T组数据

每组数据下是  n,m 。n个人(两边各)m组关系

2->接下来m行 每行 a,b,A监狱中的a ,不能与B中的b 一起



刚开始拿到这一道题的时候一点思路也没有,如何能够做。

首先发现几个事实:

1.n数据范围很小

2.如果这个人和其他的人之间有关系,那么变动一个人就需要变动多个人,才能保证不会发生冲突

所以,定义f[ i ][ j ]表示A监狱中选i个人B监狱中选j个人是否可行,可行为1。那么现在先用dfs找出所有有关联的人,进行打包,就有点像tarjan缩点那么一点意思,因为这些人互相关联,影响一个就会影响全部,so,把每一个打包后的人看作一个物品就变成了二维的01背包,三层for递推


#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 405
#define Clear(x) memset(x,0,sizeof(x))
#define PB push_back
#include<vector>
using namespace std;
int f[maxn][maxn],dfn[maxn],l[maxn],r[maxn],n,m,q,p;
vector<int>g[maxn];

void dfs(int u){
	dfn[u]=1;
	if(u<=n)p++;
	else q++;
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(dfn[v])continue;
		dfs(v);
	}
}

int main(){
	int T;
	cin>>T;
	while(T--){
		int cnt=0;
		cin>>n>>m;
		Clear(f);Clear(dfn);Clear(l);Clear(r);
		for(int i=1;i<=n*2;i++)g[i].clear();
		for(int a,b,i=1;i<=m;i++){
			scanf("%d%d",&a,&b);
			g[a].PB(b+n);
			g[b+n].PB(a);
		}
		for(int i=1;i<=2*n;i++){
			if(dfn[i])continue;
			p=q=0;
			dfs(i);
			l[++cnt]=p;r[cnt]=q;
		}
		f[0][0]=1;
		for(int i=1;i<=cnt;i++){//两个均可
			for(int j=n/2;j>=0;j--){
				for(int k=n/2;k>=0;k--){
					if(f[j][k]==1){
						f[j+l[i]][k+r[i]]=1;
					}	
				}
			}
		}
		/*for(int i=1;i<=cnt;i++){//也可以,看习惯
			for(int j=n/2;j>=l[i];j--){
				for(int k=n/2;k>=r[i];k--){
					if(f[j-l[i]][k-r[i]])f[j][k]=1;
				}
			}
		}*/
		for(int i=n/2;i>=0;i--){
			if(f[i][i]){
				printf("%d\n",i);
				break;
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值