• 【洛谷P2805】植物大战僵尸


    题目

    现在,我们将要考虑的问题是游戏中 Zombies 对 Plants 的进攻,请注意,本题中规则与实际游戏有所不同。游戏中有两种角色,Plants 和 Zombies,每个 Plant 有一个攻击位置集合,它可以对这些位置进行保护;而 Zombie 进攻植物的方式是走到植物所在的位置上并将其吃掉。

    游戏的地图可以抽象为一个 (N)(M) 列的矩阵,行从上到下用 (0)(N–1) 编号,列从左到右用 (0)(M–1) 编号;在地图的每个位置上都放有一个 Plant,为简单起见,我们把位于第 (r) 行第 (c) 列的植物记为 (P_{r, c})

    Plants 分很多种,有「攻击类」「防守类」和「经济类」等等。为了简单的描述每个 Plant,定义 (operatorname{Score})(operatorname{Attack}) 如下:

    • (operatorname{Score}(P_{r, c})) — Zombie 击溃植物 (P_{r, c}) 可获得的能源。
      (operatorname{Score}(P_{r, c})) 为非负整数,则表示击溃植物 (P_{r, c}) 可获得能源 (operatorname{Score}(P_{r, c})),若为负数表示击溃 (P_{r, c}) 需要付出能源 (-operatorname{Score}(P_{r, c}))

    • (operatorname{Attack}(P_{r, c})) — 植物 (P_{r, c}) 能够对 Zombie 进行攻击的位置集合。

    Zombies 必须从地图的右侧进入,且只能沿着水平方向进行移动。Zombies 攻击植物的唯一方式就是走到该植物所在的位置并将植物吃掉。因此 Zombies 的进攻总是从地图的右侧开始。也就是说,对于第 (r) 行的进攻,Zombies 必须首先攻击 (P_{r, M-1});若需要对 (P_{r, c})(0 le c < m - 1))攻击,必须将 (P_{r,M-1}, P_{r, M-2} cdots P_{r, c+1}) 先击溃,并移动到位置 ((r, c)) 才可进行攻击。

    在本题的设定中,Plants 的攻击力是无穷大的,一旦 Zombie 进入某个 Plant 的攻击位置,该 Zombie 会被瞬间消灭,而该 Zombie 没有时间进行任何攻击操作。因此,即便 Zombie 进入了一个 Plant 所在的位置,但该位置属于其他植物的攻击位置集合,则 Zombie 会被瞬间消灭而所在位置的植物则安然无恙(在我们的设定中,Plant 的攻击位置不包含自身所在位置,否则你就不可能击溃它了)。

    Zombies 的目标是对 Plants 的阵地发起进攻并获得最大的能源收入。每一次,你可以选择一个可进攻的植物进行攻击。本题的目标为,制定一套 Zombies 的进攻方案,选择进攻哪些植物以及进攻的顺序,从而获得最大的能源收入。

    思路

    最大权闭合子图模板题。
    发现如果要吃掉 ((i,j)) 的植物有两个限制:((i,j+1)) 被吃掉,且所有保护 ((i,j)) 的植物全部被吃掉。
    如果位置 ((i,j)) 的植物价值非负,那么我们从源点向它连一条边;否则将他向汇点连一条边,流量均为其价值的绝对值。
    然后考虑上述两种限制:显然如果 ((x_1,y_1)) 限制了 ((x_2,y_2)),我们可以从 ((x_2,y_2))((x_1,y_1)) 连一条流量为 (+infty) 的边。这样最大权闭合子图的模型就建立了。
    但是我们还需要考虑如果有若干植物互相保护形成了环,那么它们是一定都不可以选的,且这个环上任意植物保护的不在环上的植物都不可以选。
    所以我们可以先拓扑排序求出环,将环上的点标记好,不加入网络图中即可。
    注意拓扑排序需要将网络图上的边反向之后跑,因为网络图上的一条限制的边 (x o y) 指的是 (x)(y) 保护。

    代码

    #include <bits/stdc++.h>
    #define mp make_pair
    using namespace std;
    
    const int N=610,M=1000010,Inf=1e9;
    int n,m,maxf,tot=1,S,T,val[35][35],deg[N],head[N],cur[N],dep[N];
    vector<pair<int,int> > pro[35][35];
    
    struct edge
    {
    	int next,to,flow;
    }e[M];
    
    int ID(int x,int y)
    {
    	return (x-1)*m+y;
    }
    
    void add(int from,int to,int flow)
    {
    	e[++tot].to=to;
    	e[tot].flow=flow;
    	e[tot].next=head[from];
    	head[from]=tot;
    }
    
    void topsort()
    {
    	queue<int> q;
    	for (int i=1;i<N;i++)
    		if (!deg[i]) q.push(i);
    	while (q.size())
    	{
    		int u=q.front(); q.pop();
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			deg[v]--;
    			if (!deg[v]) q.push(v);
    		}
    	}
    }
    
    bool bfs()
    {
    	memcpy(cur,head,sizeof(head));
    	memset(dep,0x3f3f3f3f,sizeof(dep));
    	queue<int> q;
    	q.push(S); dep[S]=0;
    	while (q.size())
    	{
    		int u=q.front(); q.pop();
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (e[i].flow && !deg[v] && dep[v]>Inf)
    			{
    				dep[v]=dep[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dep[T]<Inf;
    }
    
    int dfs(int x,int flow)
    {
    	if (x==T) return flow;
    	int res,used=0;
    	for (int i=cur[x];~i;i=e[i].next)
    	{
    		cur[x]=i;
    		int v=e[i].to;
    		if (e[i].flow && dep[v]==dep[x]+1)
    		{
    			res=dfs(v,min(e[i].flow,flow-used));
    			used+=res;
    			e[i].flow-=res; e[i^1].flow+=res;
    			if (used==flow) return flow;
    		}
    	}
    	return used;
    }
    
    void dinic()
    {
    	while (bfs())
    		maxf-=dfs(S,Inf);
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	S=N-1; T=N-2;
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    		for (int j=1,t,x,y;j<=m;j++)
    		{
    			scanf("%d%d",&val[i][j],&t);
    			if (val[i][j]>=0)
    			{
    				add(ID(i,j),S,1);
    				deg[S]++;
    			}
    			else
    			{
    				add(T,ID(i,j),1);
    				deg[ID(i,j)]++;
    			}
    			if (j<m)
    			{
    				add(ID(i,j+1),ID(i,j),1);
    				deg[ID(i,j)]++;
    			}
    			while (t--)
    			{
    				scanf("%d%d",&x,&y);
    				x++; y++;
    				pro[i][j].push_back(mp(x,y));
    				add(ID(i,j),ID(x,y),1);
    				deg[ID(x,y)]++;
    			}
    		}
    	topsort();
    	memset(head,-1,sizeof(head));
    	tot=1;
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)
    			if (!deg[ID(i,j)])
    			{
    				if (val[i][j]>=0)
    				{
    					add(S,ID(i,j),val[i][j]);
    					add(ID(i,j),S,0);
    				}
    				else
    				{
    					add(ID(i,j),T,-val[i][j]);
    					add(T,ID(i,j),0);
    				}
    				if (j<m && !deg[ID(i,j+1)])
    				{
    					add(ID(i,j),ID(i,j+1),Inf);
    					add(ID(i,j+1),ID(i,j),0);
    				}
    				for (int k=0;k<pro[i][j].size();k++)
    				{
    					int x=pro[i][j][k].first,y=pro[i][j][k].second;
    					add(ID(x,y),ID(i,j),Inf);
    					add(ID(i,j),ID(x,y),0);
    				}
    			}
    	dinic();
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)
    			if (!deg[ID(i,j)] && val[i][j]>0) maxf+=val[i][j];
    	printf("%d",maxf);
    	return 0;
    }
    
  • 相关阅读:
    electron-vue脚手架项目初始化时提示: Failed to download repo simulatedgreg/electron-vue: Response code Server Error)
    SqlServer在附加数据库时提示:无法打开物理文件**.mdf 操作系统错误拒绝访问
    ASP.NET的项目怎样在WindowsServer上部署
    Express中错误处理中间件的使用
    Express中间件的应用-路由保护(登录限制)、网站维护、页面404
    ASP.NET中序列化与反序列化-以显示上一次登录的信息为例
    ASP.NET中使用Cache类来缓存页面的信息
    ASP.NET中使用一般处理程序ashx作为后台处理实现响应get和post请求
    ASP.NET中实现复用代码自定义用户控件UserControl的使用
    js加载更多
  • 原文地址:https://www.cnblogs.com/stoorz/p/14039032.html
Copyright © 2020-2023  润新知