湖南省第八届省赛,最小生成树Prim算法+利用二进制的状态枚举

题目信息:http://blog.csdn.net/acm_cxq/article/details/52192026

题意:

n个城市1--n,有m座桥可以城市,编号为1的城市是皇宫。修建每座桥的费用为c,每个城市的人口为pi。总费用为k。

求从皇宫出发,修建桥连接尽可能多的城市,城市相互直接或者间接连接通向皇宫,使得跟皇宫相连的所有城市的人口数之和为最大值,且费用不超过k。(无向图)


思路:巧妙运用二进制保存所有城市的状态,n个城市,每个城市选或不选,有2^n种可能。

1代表选,0代表不选。(1号城市必须入选)

如n等于4时,4个城市。

那么 1<<4等于16种可能,

如1101,代表第1,3,4城市入选,第二个城市不选。

然后将选入的城市的编号保存在一个数组里面,用prim算法求得入选城市相连后的花费。

在所有状态中,找出总人口最多的那个状态。


代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 20;
const int INF = 999999999;
int n;//城市,编号1--n,1为皇帝宫
int m;//可以修的路
int cost;//最大花费

int weight[maxn];//每个城市对应的人口数
int graph[maxn][maxn];//修建两个城市间路的费用

int city[maxn];//被选的城市
int state;//状态
int COST,temp;//被选的城市的总人数

int pos;//被选的城市个数

void init() {
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=n; j++) {

			graph[i][j] = INF;

			if(i==j) graph[i][j] = 0;
		}
	}
}

int prim(int n,int pos) {
	int mincost[maxn];
	bool used[maxn];

	for(int i=0; i<n; i++) {
		mincost[city[i]] = graph[city[i]][pos];
		used[city[i]] = false;
	}
	mincost[pos] = 0;

	int res = 0;
	while(true) {
		int v = -1;
		//从未被true的顶点中选取从已经选的点到其权值最小的顶点
		for(int u=0; u<n; u++) {
			if(!used[city[u]]&&(v==-1||mincost[city[u]]<mincost[city[v]])) v = u;

		}
		if(v==-1) break;//更新完了 

		used[city[v]] = true;
		res+=mincost[city[v]];
		if(res>cost) return -1;
		for(int u = 0; u<n; u++) {
			mincost[city[u]] = min(mincost[city[u]],graph[city[v]][city[u]]);
		}

	}


	return res;

}

int main() {
	int cases;
	cin>>cases;
	int a,b,c;


	while(cases--) {
		cin>>n>>m>>cost;
		memset(city,0,sizeof(city));
		 
		for(int i=1; i<=n; i++) {
			cin>>weight[i];
		}

		for(int i=0; i<m; i++) {
			cin>>a>>b>>c;
			graph[a][b] = graph[b][a] = min(graph[a][b],c);
		}

		state = 1<<n;//枚举2的n次方中可能性 
		COST = -1;

		for(int i = state-1; i>0; i--) {
			//第一个城市必须入选,不然就进入下一个状态
			if(!(i&1)) continue;//如果这些状态中第一个城市没有入选,那么直接跳过 

			pos = 0;
			temp = 0;
			for(int j=0; j<n; j++) {
				//判断哪些城市入选 
				if((i>>j)&1) {
					city[pos++] = j+1;//表示第j+1个城市入选
					temp += weight[j+1];//加上第j+1个城市的人口数
				}
			}

			int cost = prim(pos,city[0]);//以第一个城市到各个被选城市的最小花费 
			
			//如果总花费不大于给定的总价,选择总人数最多的方案 
			if(cost!=-1) {
				COST = max(COST,temp);
				if(i==state-1) break;//如果所有城市都入选,那人口总数必定是最大的!!!
			}

		}

		cout<<COST<<endl;

	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值