bzoj4006

时间:2023-03-10 04:57:21
bzoj4006

斯坦纳树

比之前要求高了一些

其实利用斯坦纳树的dp[i][s]以i为根,S为状态就行了,先跑一遍斯坦纳树,预处理出dp数组,记住每个S的最小值,然后再dp,这里dp必须要求同一种颜色的状态都必须在S里,然后跑枚举子集就行了

#include<bits/stdc++.h>
using namespace std;
const int N = , inf = 0x3f3f3f3f;
struct edge {
int nxt, to, w;
} e[N * ];
struct points {
int x, c;
} a[N];
int n, m, p, cnt = ;
int head[N], sum[], vis[N], ans[ << ], dp[N][ << ], tmp[];
bool check(int S)
{
memset(tmp, , sizeof(tmp));
for(int i = ; i < p; ++i) if(S >> i & ) ++tmp[a[i].c];
for(int i = ; i <= p; ++i) if(tmp[i] && sum[i] != tmp[i]) return false;
return true;
}
void link(int u, int v, int w)
{
e[++cnt].nxt = head[u];
head[u] = cnt;
e[cnt].to = v;
e[cnt].w = w;
}
int main()
{
scanf("%d%d%d", &n, &m, &p);
for(int i = ; i <= m; ++i)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
link(u, v, w);
link(v, u, w);
}
memset(dp, 0x3f3f, sizeof(dp));
for(int i = ; i < p; ++i)
{
scanf("%d%d", &a[i].c, &a[i].x);
dp[a[i].x][ << i] = ;
++sum[a[i].c];
}
for(int S = ; S < ( << p); ++S)
{
queue<int> q;
for(int i = ; i <= n; ++i)
{
for(int S0 = S; S0; S0 = (S0 - ) & S)
dp[i][S] = min(dp[i][S], dp[i][S ^ S0] + dp[i][S0]);
if(dp[i][S] < inf)
{
vis[i] = ;
q.push(i);
}
}
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = ;
for(int i = head[u]; i; i = e[i].nxt) if(dp[u][S] + e[i].w < dp[e[i].to][S])
{
dp[e[i].to][S] = dp[u][S] + e[i].w;
if(!vis[e[i].to])
{
vis[e[i].to] = ;
q.push(e[i].to);
}
}
}
ans[S] = inf;
for(int i = ; i <= n; ++i) ans[S] = min(ans[S], dp[i][S]);
}
for(int i = ; i < ( << p); ++i)
if(check(i))
for(int S = i; S; S = (S - ) & i)
if(check(S))
ans[i] = min(ans[i], ans[i ^ S] + ans[S]);
printf("%d\n", ans[( << p) - ]);
return ;
}