题目和数据在这里: 链接: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;
}