HNOI模拟 2016.3.31 百步穿杨

时间:2021-11-02 10:02:12

题目和数据在这里: 链接:http://pan.baidu.com/s/1nu5JRzr 密码:wt2n

题目大意:给你一个棋盘,格子有三种类型:指定方向的箭塔,目标,或者空地,每个箭塔可以选中一个目标(当然每个目标只能被选一次),求最大收益

额外条件:箭塔的射程内不会有其他箭塔

限制:箭塔的攻击轨迹不能相交


发现隐藏条件:箭塔的攻击轨迹不能相交,那么每个格子只能被一个箭塔穿过。因为箭塔的射程内不会有其他箭塔,

那么若一个格子在一个向右的箭塔的攻击范围内,就不可能在一个向左的箭塔的范围里。

所以我们实际上只用给每个格子定方向:横向或纵向。

这样方向就只有两种,问题显然可以转化成网络流-最小割。

考虑限制条件:

1.每个格子只有一种方向:这个显然可以拆点解决

2.每个箭塔只能选一个目标:设射程内点权最大值为Max,将Max加入总贡献。我们将射程内的点按顺序连起来,每个点和下个点的边权为Max-这个点点权,这条边被割代表选择了该点。若这条链跟汇点(或者源点)不相连,则表示射程里的每个点都不在其他箭塔的攻击范围内。

3.每个箭塔代表的方向已经确定

总贡献-最小割即为答案

代码如下:

/*
* @Author: 逸闲
* @Date:   2016-04-21 19:35:12
* @Last Modified by:   逸闲
* @Last Modified time: 2016-04-21 20:41:46
*/

#include "cstdio"
#include "cstdlib"
#include "iostream"
#include "algorithm"
#include "cstring"
#include "queue"

using namespace std;

#define INF 0x3F3F3F3F
#define MAX_SIZE 55
#define Eps
#define Mod
#define Get(x, a) (x ? x -> a : 0)

inline int Get_Int()
{
	int Num = 0, Flag = 1;
	char ch;
	do
	{
		ch = getchar();
		if(ch == '-')
			Flag = -Flag;
	}
	while(ch < '0' || ch > '9');
	do
	{
		Num = Num * 10 + ch - '0';
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9');
	return Num * Flag;
}

namespace Flow_Network
{
	class Edge
	{
	public:
		int To, Next, C;
	}Edges[MAX_SIZE * MAX_SIZE * MAX_SIZE * 2];

	int S, T, Total = 1;
	int Distance[MAX_SIZE * MAX_SIZE * 2], Front[MAX_SIZE * MAX_SIZE * 2];

	inline void Add_Edge(int From, int To, int C)
	{
		Edges[++Total].To = To;
		Edges[Total].Next = Front[From];
		Edges[Total].C = C;
		Front[From] = Total;
	}

	inline void Add_Edges(int From, int To, int C)
	{
		Add_Edge(From, To, C);
		Add_Edge(To, From, 0);
	}

	inline bool BFS()
	{
		queue<int> Queue;
		memset(Distance, 0, sizeof(Distance));
		Distance[S] = 1;
		Queue.push(S);
		while(!Queue.empty())
		{
			int Now = Queue.front();
			Queue.pop();
			for(int i = Front[Now]; i; i = Edges[i].Next)
				if(Edges[i].C && !Distance[Edges[i].To])
				{
					Distance[Edges[i].To] = Distance[Now] + 1;
					Queue.push(Edges[i].To);
				}
		}
		return Distance[T];
	}

	int DFS(int Now, int In)
	{
		if(Now == T)
			return In;
		int Rest = In;
		for(int i = Front[Now]; i; i = Edges[i].Next)
			if(Edges[i].C && Distance[Edges[i].To] == Distance[Now] + 1)
			{
				int Increment = DFS(Edges[i].To, min(Edges[i].C, Rest));
				Rest -= Increment;
				Edges[i].C -= Increment;
				Edges[i ^ 1].C += Increment;
				if(!Rest)
					return In;
			}
		if(Rest == In)
			Distance[Now] = 0;
		return In - Rest;
	}

	inline int Max_Flow()
	{
		int Flow = 0;
		while(BFS())
			Flow += DFS(S, INF);
		return Flow;
	}
}

int N, M, Total, Ans;
int Movex[] = {0, -1, 1, 0, 0}, Movey[] = {0, 0, 0, -1, 1};

char Map[MAX_SIZE][MAX_SIZE];

inline int Point(int x, int y)
{
	return (x - 1) * M + y;
}

inline int Find(char ch)
{
	if(ch == 'A')
		return 1;
	else if(ch == 'V')
		return 2;
	else if(ch == '<')
		return 3;
	else if(ch == '>')
		return 4;
	return 0;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("archery.in", "r", stdin);
	freopen("archery.out", "w", stdout);
#endif
	cin >> N >> M;
	for(int i = 1; i <= N; ++i)
		scanf("%s", Map[i] + 1);
	Total = N * M;
	Flow_Network::S = 0;
	Flow_Network::T = Total * 2 + 1;
	for(int i = 1; i <= N; ++i)
		for(int j = 1; j <= M; ++j)
		{
			if(Map[i][j] >= '0' && Map[i][j] <= '9')
				Map[i][j] -= '0';
			Flow_Network::Add_Edges(Point(i, j), Point(i, j) + Total, INF);
		}
	for(int i = 1; i <= N; ++i)
		for(int j = 1; j <= M; ++j)
			if(int Direction = Find(Map[i][j]))
			{
				int Max = 0, Flag = Direction <= 2;
				if(Flag)
					Flow_Network::Add_Edges(Flow_Network::S, Point(i, j), INF);
				else
					Flow_Network::Add_Edges(Point(i, j) + Total, Flow_Network::T, INF);
				for(int x = i + Movex[Direction], y = j + Movey[Direction]; Map[x][y]; x += Movex[Direction], y += Movey[Direction])
					if(Map[x][y] <= 9)
						Max = max(Max, (int)Map[x][y]);
				for(int x = i + Movex[Direction], y = j + Movey[Direction], a = i, b = j; Map[x][y]; a = x, b = y, x += Movex[Direction], y += Movey[Direction])
				{
					int C = Max;
					if(Map[a][b] <= 9)
						C -= Map[a][b];
					if(Flag)
						Flow_Network::Add_Edges(Point(a, b), Point(x, y), C);
					else
						Flow_Network::Add_Edges(Point(x, y) + Total, Point(a, b) + Total, C);
				}
				Ans += Max;
			}
	cout << Ans - Flow_Network::Max_Flow() << endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}