[NOI2009] 植物大战僵尸
在n*m的地图当中,每个位置都有一个植物,每个植物有一个价值,这个价值可能是正数也可能是负数。每个植物可能有几个攻击的位置,只有在除掉这个植物之后才可以到那几个位置去。作为僵尸,只能从右往左攻击,每次可以除掉一个植物,找到方案使除掉的植物总价值最大。
数据范围n<=20 m<=30
最大权闭合子图入门题,用最小割来解决。
如果我们要除掉一个植物的话,那么首先我们需要除掉的是在这个植物右边的所有植物以及所有可以攻击到这个位置的植物,也就是,如果选择了这个植物,那么这些植物就都得选择。这就是一个最大权闭合子图问题,可以用最小割来进行求解。
按照最小割的一般方法,先把所有的能选的都选上,再去割那些不合法的。在这个问题上,首先将所有的正数的植物全部选了,计算一个sum,然后由源点向所有正数点进行连边,边容量为植物价值,由所有的负数点向汇点进行连边,边容量为植物价值的相反数。然后将闭合子图的关系建出来,容量为inf,求出最小割就可以了。
这样的原理在于:首先我们是默认选择了所有的正数,那么除了这些植物以外,基本上还有很多负数的植物要选择,这就要作出抉择了,要么取消一个正数的植物,就是割源点的边,要么就是选择它并且承担需要选择的负数的植物,就是割汇点的边。最小割的值和sum的价值取差值就是答案。
这个问题中,还要考虑到有几个植物搞小团体互相保,就要直接排除它们。这要用到拓扑排序去掉环,注意拓扑排序的入度是从右到左的,而建边是从左到右的。
这个代码只得了90,有一个点T了,感觉应该是要把环除去重新建边,再跑网络流,不过我写累了,就这样吧。
const int N = 610, M = 1e6, inf = 1e9 + 7;
int n, m, s, t, sum = 0;
int a[N], head[N], now[N], num = -1, dis[N], in[N];
bool vis[N], tp[N];
struct edge
{
int nxt, to, flow;
};
edge e[M];
void addEdge(int x, int y, int z)
{
e[++ num].nxt = head[x];
head[x] = num;
e[num].to = y;
e[num].flow = z;
}
inline int getNum(int x, int y)
{
return x * m + y;
}
bool bfs()
{
for (int i = 0; i <= n * m + 1; i ++)
dis[i] = inf;
dis[s