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 Pi, Si .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 xi, yi, ci, 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;
}