【网络流】CEOI2009 all

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值