JZOJ 3575
【题目大意】
在一个R*C的网格上(R、C<=70),某些方格中有部落。现在他们要相互结盟。每个部落可以与它上下左右四个方向的部落结盟。下图表示了四种部落每种可能的结盟方式。(O为部落,X为部落的盟友)。要求输出任意一种方案或无解。
【算法讨论】
这题在考试时A了,后来想想还是觉得应该理理思路,毕竟考试时想+写用了3h。
因为每个部落只能与相邻部落连边,所以我们可以对整个网格黑白染色。
增加源点和汇点,源点向黑色部落连一条边,容量为该部落的结盟数;白色部落向汇点连一条边,容量为该部落的结盟数;黑色部落向每个与它相邻的部落(都是白色的)连一条边权为1的边。这样子做一次最大流,若满流我们就可以通过残余网络找到答案,否则无解。
这样的构图对于humans的部落求出来的方案可能不合法。观察humans的结盟方案,可以看成在上下两个方向找一个部落结盟,左右两个方向找一个部落结盟。由于两种方式互不影响,我们可以把这种部落拆成两个点,一个点负责与左右部落的结盟,另一个点负责与上下部落的结盟。我们可以把这种部落拆成两个点,一个点负责与左右部落的结盟,另一个点负责与上下部落的结盟。无论是黑点还是白点,humans的点都要拆了再连边。
然后使用最大流算法即可求出答案。
预处理时要记录原图中的相邻点的,输出方案时找到
【时空复杂度】
时间复杂度:O((R*C)^3)
空间复杂度:O(R*C)
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for (int i = a;i < b;i ++)
#define OP(x) ((((x) - 1) ^ 1) + 1)
#define OP_DIR(x) ((x + 2) & 3)
using namespace std;
const int MAXL = 75;
const int MAXN = MAXL * MAXL * 2 + 10;
const int MAXM = MAXN * 12;
const int fx[] = {0, 1, 0, -1};
const int fy[] = {1, 0, -1, 0};
const int INF = 0x3f3f3f3f;
int N,S,T,n,m;
int id[MAXL + 1][MAXL + 1][2],map[MAXL + 1][MAXL + 1],edge_id[MAXL + 1][MAXL + 1][4];
int begin[MAXN + 1], end[MAXM + 1], next[MAXM + 1], c[MAXM + 1];
int tot_flow[2];
int tot = 0;
int ID(int x, int y, int flag)
{
if (id[x][y][flag]) return id[x][y][flag];
return id[x][y][flag] = N ++;
}
void Insert(int x,int y,int z)
{
tot ++;
next[tot] = begin[x];
begin[x] = tot;
end[tot] = y;
c[tot] = z;
}
void AddEdge(int x, int y, int z)
{
Insert(x,y,z);
Insert(y,x,0);
}
int cur[MAXN + 1], d[MAXN + 1], pre[MAXN + 1], a[MAXN + 1], cnt[MAXN + 1];
int sap()
{
int flow = 0;
int now, tmp, u;
a[u = S] = INF;
cnt[0] = N;
memcpy(cur, begin, sizeof(begin[0]) * N);
while (d[S] < N)
{
for (now = cur[u]; now; now = next[now])
if (c[now] && d[u] == d[end[now]] + 1) break;
if (now)
{
tmp = end[now];
pre[tmp] = cur[u] = now;
a[tmp] = min(a[u], c[now]);
if ((u = tmp) == T)
{
flow += (tmp = a[T]);
do
{
c[pre[u]] -= tmp;
c[OP(pre[u])] += tmp;
u = end[OP(pre[u])];
} while (u != S);
a[S] = INF;
}
}
else
{
if ((--cnt[d[u]]) == 0) break;
cur[u] = begin[u];
d[u] = N;
for (now = begin[u]; now; now = next[now])
if (c[now] && d[u] > d[end[now]] + 1)
d[u] = d[end[now]] + 1, cur[u] = now;
cnt[d[u]] ++;
if (u != S) u = end[OP(pre[u])];
}
}
return flow;
}
bool ans;
void Solve()
{
ans = 1;
if (tot_flow[0] != tot_flow[1] || sap() != tot_flow[0]) ans = 0;
}
const int UP = 3;
const int DOWN = 1;
const int LEFT = 2;
const int RIGHT = 0;
void Print()
{
if (!ans) printf("Impossible!\n");
else
{
fo(i,0,n)
{
fo(j,0,m)
{
printf("%c", '.');
printf("%c", c[edge_id[i][j][UP]] ? 'X' : '.');
printf("%c", '.');
}
printf("\n");
fo(j,0,m)
{
printf("%c", c[edge_id[i][j][LEFT]] ? 'X' : '.');
printf("%c", map[i][j] ? 'O' : '.');
printf("%c", c[edge_id[i][j][RIGHT]] ? 'X' : '.');
}
printf("\n");
fo(j,0,m)
{
printf("%c", '.');
printf("%c", c[edge_id[i][j][DOWN]] ? 'X' : '.');
printf("%c", '.');
}
printf("\n");
}
}
}
void BuildGraph()
{
fo(i,0,n) fo(j,0,m)
if (map[i][j])
{
if ((i + j) & 1)
{
for (int k = 0; k < 4; k ++)
{
int x = i + fx[k];
int y = j + fy[k];
if (x >= 0 && x < n && y >= 0 && y < m && map[x][y])
{
AddEdge(ID(i, j, (map[i][j] == 2 ? (k & 1) : 0)), ID(x, y, (map[x][y] == 2 ? (k & 1) : 0)), 1);
edge_id[i][j][k] = edge_id[x][y][OP_DIR(k)] = tot;
}
}
if (map[i][j] == 2)
{
AddEdge(S, ID(i, j, 0), 1);
AddEdge(S, ID(i, j, 1), 1);
}
else AddEdge(S, ID(i, j, 0), map[i][j]);
}
else
{
if (map[i][j] == 2)
{
AddEdge(ID(i, j, 0), T, 1);
AddEdge(ID(i, j, 1), T, 1);
}
else AddEdge(ID(i, j, 0), T, map[i][j]);
}
tot_flow[(i + j) & 1] += map[i][j];
}
}
void Initialize()
{
scanf("%d%d", &n, &m);
fo(i,0,n)
fo(j,0,m)
scanf("%d", &map[i][j]);
N = 2;
S = 0; T = 1;
BuildGraph();
}
int main()
{
Initialize();
Solve();
Print();
return 0;
}