ZOJ3613-Wormhole Transport

Wormhole Transport

Time Limit: 3 Seconds       Memory Limit: 65536 KB

Wormhole is a kind of two-way tunnel between two planets which can make it possible to transport large amounts of resources without time. Of course, building Wormhole has lots of requirements, only some pairs of the planet can build Wormhole and it costs lots of money. Country G is a very big country which has a lot of planets. There are several resource planets in country G. One resource planet can provide for a factory, and a factory need a resource planet to provide resource. Due to various reasons, country G has build some factories in several planets without considering about resource planets. So it depends on Wormhole to transport resource if a factory can't get resource from local. It is your task to make a plan to build Wormholes which can make most factories run and use the least money in this case.

Input

The input consist of multiple cases. For each test case, the first line contains an integer N (0 < N ≤ 200) which indicates the number of planets in country G. Then followed by N lines, each line contains two integers PiSi .Pi is the number of factories in planet i, if Si =1, planet i is a resource planet; if Si =0, planet i isn't a resource planet. At most of 4 planets have factories and at most of 4 planets are resource planets. The next line contains an integer M (0 < M ≤ 5000) which indicates the number of pairs of planet which can build Wormhole. Then followed by M lines, each line contains three integers xiyici, which indicate building Wormhole between planet xi and planet yi costs ci.

Output

The output contains one line per case. It contains the maximum of factories that can get resource and the the minimum of money when the maximum of factories that can get resource.

Sample Input
2
1 0
0 1
1
1 2 3
Sample Output
1 3

Author:  LU, Yi
Contest:  ZOJ Monthly, June 2012



题意:n个星球组成的一张图,星球上有资源、工厂或者什么都没有(一个星球上可以有多个工厂,但只能有一个资源,这种星球最多8个)。每个工厂需要有一个有资源星球为它提供资源才能生产。已知在各各星球间建立运输用的虫洞的花费,求最多有多少工厂能生产同时求最小花费

解题思路:斯坦纳树,不过在统计时要注意花费的最小值应该是0


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;

struct node
{
	int x, y;
}pre, nt1;
int n, m, u, v, w, cnt1, cnt2, x[10];
int s[205], nt[10009], e[10009], val[10009], cnt;
int status;//表示0~n号节点都被选择时的状态+1  
int dis[205][1200], vis[205][1200], flag[1200], ans[1200], P[205], S[205], sum, mi, ma;
//dis[i][j]表示以i节点为根选择点集状态为j时的最小值;vis[i][j]表示i节点为点集j时是否在队列中  
queue<node> q;

void init()
{
	memset(s, -1, sizeof s);
	memset(vis, 0, sizeof vis);
	cnt1 = cnt2 = sum = cnt = 0;
	status = 1 << 8;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j < status; j++)
			dis[i][j] = INF;
}

void SPFA()
{
	while (!q.empty())
	{
		pre = q.front();
		q.pop();
		vis[pre.x][pre.y] = 0;
		for (int i = s[pre.x]; ~i; i = nt[i])
		{
			if (dis[pre.x][pre.y] + val[i] < dis[e[i]][pre.y])
			{
				dis[e[i]][pre.y] = dis[pre.x][pre.y] + val[i];
				if (!vis[e[i]][pre.y])
				{
					nt1 = { e[i],pre.y };
					q.push(nt1);
					vis[e[i]][pre.y] = 1;
				}
			}
		}
	}
}

void Steiner_Tree()
{
	for (int i = 0; i < (1 << (cnt1 + cnt2)); i++)
	{
		for (int j = 1; j <= n; j++)
		{
			for (int k = i; k; k = (k - 1) & i)
				dis[j][i] = min(dis[j][i], dis[j][k] + dis[j][i - k]);
			if (dis[j][i] != INF)
			{
				nt1 = { j,i };
				q.push(nt1);
				vis[j][i] = 1;
			}
		}
		SPFA();
	}
}

int check(int k)
{
	int sum = 0;
	for (int i = 0; k; i++, k >>= 1)
		sum += (k & 1)*(i < cnt1 ? x[i] : -1);
	return sum >= 0;
}

int get(int k)
{
	int sum = 0;
	for (int i = 0; k; i++, k >>= 1)
		sum += (k & 1)*(i < cnt1 ? 0 : 1);
	return sum;
}

int main()
{
	while (~scanf("%d", &n))
	{
		init();
		for (int i = 1; i <= n; i++)
		{
			scanf("%d%d", &P[i], &S[i]);
			if (P[i] && S[i]) sum++, S[i] = 0, P[i]--;
			if (P[i]) dis[i][1 << cnt1] = 0, x[cnt1++] = P[i];
		}
		for (int i = 1; i <= n; i++)
			if (S[i]) dis[i][1 << (cnt1 + cnt2)] = 0, cnt2++;
		scanf("%d", &m);
		for (int i = 1; i <= m; i++)
		{
			scanf("%d%d%d", &u, &v, &w);
			nt[cnt] = s[u], s[u] = cnt, e[cnt] = v, val[cnt++] = w;
			nt[cnt] = s[v], s[v] = cnt, e[cnt] = u, val[cnt++] = w;
		}
		Steiner_Tree();
		for (int i = 0; i < (1 << (cnt1 + cnt2)); i++)
		{
			flag[i] = check(i), ans[i] = INF;
			for (int j = 1; j <= n; j++) ans[i] = min(ans[i], dis[j][i]);
		}
		ma = 0, mi = 0;
		for (int i = 0; i < (1 << (cnt1 + cnt2)); i++)
		{
			if (flag[i])
			{
				for (int j = i; j; j = (j - 1) & i)
					if (flag[j] && flag[i - j]) ans[i] = min(ans[i], ans[j] + ans[i - j]);
				int temp = get(i);
				if (temp > ma || (ma == temp&&mi > ans[i]))
					ma = temp, mi = ans[i];
			}
		}
		printf("%d %d\n", ma + sum, mi);
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值