通信团体问题-非递归回溯

/*
通信团体问题: 
   有一家通信公司近来要推出一项优惠活动,凡是在某个群体中相互通话的用
户可以得到某种通话费折扣优惠。A、B两个用户相互通话是指其中之一(如A)与另一个人(如B)打过电话,而不必要求B打电话给A。 
   一个群体G要满足通讯公司的优惠政策,必须满足两个条件: ① G中任何两个用户通过话; ② G是团体,即如在加一个G外的人进去,所得新群体是不满足条件①的。 
   该通信公司还对它的用户团体中用户数最大的团体实行更优惠的政策:手机单项收费,免一年的月租费。
   为了搞好这项工作,这家公司委托您帮助他们进行调研和统计。他们给您的任务是计算出这家通信公司的所有用户构成的群体中最大团体的用户数,以给予最优惠的待遇。
   最大团体是所有团体中用户数最多的团体,这样的团体可能不止一个,但所有最大团体中用户数是一样的。 

输入 
   输入有若干组测试数据,每组测试数据的第一行上有一个整数n(1≤n≤50),
是通信公司的用户数。接下来的n行是这n个人的通信状况,其第i行是长为n的0,1序列,序列之间无空格。
该行第j个位置的数如为1,表示i与j通过电话;如为0则表示未通过话。相邻两组测试之间无空行。输入直到文件结束。 

输出 
   对输入中的每组测试数据,输出对应的最大团体的用户数m。对第i组测试数据,先在一行上输出“Case i”,接着输出m,见样例。 

输入样例                                  输出样例 
5 											Case i:3 
01011 
10101 
01001 
10001 					
11110 
*/

/*
解法:属于子集数问题,用非递归回溯实现。
参考《ACM/ICPC程序设计与分析》 沈云付编著
*/
#include <iostream>
#include <fstream>
using namespace std;
#define MAX 20
//二维数组input表示图的邻接矩阵,一维数组bestn表示当前最优解,bestn表示当前最大团的点数,
//一维数组current表示当前解,currentn为当前团的点数
int input[MAX][MAX], best[MAX], bestn, current[MAX], currentn;

//ifstream fcin("data.txt");
//#define cin fcin
//非递归回溯实现
int Maxm(int n)
{
	int i=0,j;
	bool ok;
	currentn=0;
	while(i>=0)
	{
		ok=true;
		while(i == n) //到达叶子节点,回溯
		{
			if(currentn > bestn)
			{
				bestn = currentn;
				for(j=0; j<n; ++j)
				{
					best[j] = current[j];
				}
			}

			i--;
			while(i>=0 && current[i] == 0) i--;
			if(i == -1)  return bestn;
			current[i] = 0; ++i;
			currentn--;
		}

		for(j=0; j<i; ++j)
		{
			if(current[j]==1 && input[i][j]==0)	//当前团体内的j是否与i相连
			{

				ok = false;
				break;
			}
		}
		if(ok)	//i跟团体内所有都相连
		{
			current[i]=1;
			++i;
			currentn++;
		}
		else
		{
			if(currentn + (n-i) > bestn)// 当前团个数+未探索的节点数> 当前最优团体数
			{
				current[i] = 0;	//不相连则i不属于这个团体
				++i;
			}
			else	//回溯
			{
				i--;
				while(i>=0 && current[i] == 0) i--;
				if(i == -1)  return bestn;
				current[i] = 0; ++i;
				currentn--;
			}
		}

	}
}
int main()
{
	int n,i,j,num=0;
	char c[MAX][MAX];
	while(cin>>n)
	{
		num++;
		bestn=0;
		currentn=0;
		cin.get();
		for(i=0; i<n; ++i) 
		{
			cin.getline(c[i], MAX);
			current[i] = 0;
		}
		for(i=0; i<n; ++i)
		{
			for(j=0; j<n; ++j)
			{
				input[i][j] = (int)(c[i][j] - '0');
				cout<<input[i][j];
			}
			cout<<endl;
		}

		cout<<"Case "<<num<<": ";
		cout<<Maxm(n)<<endl;
	}
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值