您的位置:首页 > 其它

HDU-3311-Dig The Wells

2016-11-07 01:34 295 查看
ACM模版

描述



题解

斯坦纳树,模版题,状压dp,还是无法很好地理解,找了大牛们的blog,感觉好高大上……

以下来自Staginner大牛的博客……

这个和一般的斯坦纳树的题目不同的地方在于挖井要加点权,但是仔细分析一下不难发现,如果1-N作为斯坦纳树的叶子节点一共有两种状态,要么挖井要么不挖井,而其他点做为叶子节点只有一种状态,就是必须挖井,否则由于这个节点就可以被删去就一定不会是最优的方案。在dp之前如果将这些叶子节点的状态都初始化好的话,后面的内容就和普通的斯坦纳树的题目就没有什么区别了,只需要考虑边权即可,另外最后合并连通块dp的时候要判断一下当前集合内是否有水,而且要用两个有水的点集合成当前这个点集。

由于要判一个连通块内有没有水,所以要多开一个二进制位表示有没有水。

另外推荐一个感觉讲斯坦纳树讲得不错的博客:http://endlesscount.blog.163.com/blog/static/821197872012525113427573/


代码

#include <stdio.h>
#include <string.h>

#define INF 0x3f3f3f3f

const int MAXQ = 74000;
const int MAXD = 1010;
const int MAXM = 10010;
const int MAXS = 74;

int N, M, P, first[MAXD], e, next[MAXM], v[MAXM], w[MAXM];
int bit[MAXD], q[MAXQ + 10], front, rear, inq[MAXD][MAXS];
int f[MAXD][MAXS], dp[MAXS];

void add(int x, int y, int z)
{
v[e] = y, w[e] = z;
next[e] = first[x], first[x] = e++;
}

void init()
{
int i, x, y, z;
memset(f, 0x3f, sizeof(f));
memset(bit, 0, sizeof(bit));
for (i = 1; i <= N + M; i++)
{
if (i <= N)
{
bit[i] = 1 << (i - 1);
}
scanf("%d", &z);
if (i <= N)
{
f[i][bit[i]] = 0, f[i][bit[i] | 1 << N] = z;
}
else f[i][1 << N] = z;
}
memset(first, -1, sizeof(first));
e = 0;
for (i = 0; i < P; i++)
{
scanf("%d%d%d", &x, &y, &z);
add(x, y, z), add(y, x, z);
}
}

int Min(int x, int y)
{
return x < y ? x : y;
}

void spfa()
{
int i, x, st, y, nst;
while (front != rear)
{
x = q[front] & 1023, st = q[front] >> 10;
inq[x][st] = 0;
++front > MAXQ ? front = 0 : 0;
for (i = first[x]; i != -1; i = next[i])
{
y = v[i], nst = st | bit[y];
if (f[x][st] + w[i] < f[y][nst])
{
f[y][nst] = f[x][st] + w[i];
if (st == nst && !inq[y][nst])
{
q[rear ++] = nst << 10 | y, inq[y][nst] = 1;
rear > MAXQ ? rear = 0 : 0;
}
}
}
}
}

void solve()
{
int i, j, k, nn = 1 << (N + 1);
front = rear = 0;
memset(inq, 0, sizeof(inq));
for (i = 0; i < nn; i++)
{
for (j = 1; j <= N + M; j++)
{
for (k = i - 1 & i; k; k = k - 1 & i)
{
f[j][i] = Min(f[j][i], f[j][k | bit[j]] + f[j][i - k | bit[j]]);
}
if (f[j][i] < INF)
{
q[rear ++] = i << 10 | j, inq[j][i] = 1;
rear > MAXQ ? rear = 0 : 0;
}
}
spfa();
}

memset(dp, 0x3f, sizeof(dp));
for (i = 0; i < nn; i++)
{
for (j = 1; j <= N + M; j++)
{
dp[i] = Min(dp[i], f[j][i]);
}
}
for (i = 0; i < nn; i ++)
{
if (i & 1 << N)
{
for (j = i - 1 & i; j; j = j - 1 & i)
{
if (j & 1 << N)
{
dp[i] = Min(dp[i], dp[j] + dp[i - j | 1 << N]);
}
}
}
}
printf("%d\n", dp[(1 << (N + 1)) - 1]);
}

int main()
{
while (scanf("%d%d%d", &N, &M, &P) == 3)
{
init();
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  斯坦纳树 状压dp