【重庆省选2008】传感器网络 && 网络流

2622: 【重庆省选2008】传感器网络

时间限制: 1 Sec   内存限制: 128 MB
提交: 33   解决: 8
[ 提交][ 状态][ 我的提交]

题目描述

 一个无线传感器网络由若干独立采集数据的设备和一个控制中心组成。每个设备必须把采集到的数据传到控制中心处理,但由于设备限制,并不是每台设备都可以与控制中心直接相连。为了解决这一问题,你将传感器网络设计成树状结构。树根为控制中心,而每台设备恰好有一个父亲节点(要么为控制中心,要么为另一台设备)。一台设备的儿子设备个数称为它的负载级别。所有设备(注意,控制中心不是设备)的负载级别的最大值称为网络的负载级别。

    你的任务是让整个网络的负载级别尽量小。

输入

 第一行包含一个整数N,即设备的个数。第二行包含N个字符,表示每台设备是否能直接连接到控制中心。以下N行每行N个字符,其中第i行第j个字符为'Y'当且仅当设备i可以作为设备j的儿子。如果设备i可以作为设备j的儿子,那么在任何合法的网络中,设备j一定不会是设备i的后代。换句话说,如果把这N行看作一个有向图的邻接矩阵,该图不存在有向环。

输出

 仅一行,包含N个整数,即每个设备的父亲。设备按照输入顺序编号为0~N-1,控制中心用N表示。如果有多组解,输出字典序最小解。

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

5
YNNYN
NNNNN
YNNNY
NNNYN
NNNNN
YNNYN

样例输出

5 4 3 5 0


对于100%的数据 N <= 50


由于本题提到了最大值最小问题 很容易想到二分 所以我们考虑二分最大负载

我们可以通过网络流来检验当前最大负载是否可行 建图方法如下 :

把每一个点拆成两个 把源点S 连像 i 容量为1 把n+i 连向汇点T 容量为k(最大负载)

在把每个设备i 连向其父亲对应的节点n+fa[i] 容量为1

通过检验最大流是否等于点数n 即可检验当前负载是否可行


之后的问题在于如何输出字典序最小的方案 :

由于数据中N很小 我们可以每一个节点i 的父亲fa[i]

当fa[i]被指定后 我们就不再从源点向i连边 同时父亲n+fa[i]流向T的容量减1

然后我们再检验现在图中的点数是否等于现在的最大流流量 若可以则i的父亲就确定为fa[i] 通过这样的枚举 检验每一个点即可


#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<queue>
#define SF scanf
#define PF printf
using namespace std;
typedef long long LL;
const int MAXN = 100;
const int INF = 0x3f3f3f3f;
int n, m, S, T, k;
char s[MAXN+10], s1[MAXN+10];
int A[MAXN+10][MAXN+10], fa[MAXN+10];
template <int maxn>
struct ISAP {
    int n, m, s, t;
    int cap[maxn+10][maxn+10];
    int gap[maxn+10], d[maxn+10];
    void init(int N) {
        memset(cap, 0, sizeof(cap));
    }
    void add(int u, int v, int c) {
        cap[u][v] = c;
    }
    void init() {
        queue <int> q;
        memset(gap, 0, sizeof(gap));
        memset(d, -1, sizeof(d));
        d[t] = 0; q.push(t);
        while(!q.empty()) {
            int u = q.front(); q.pop();
            gap[d[u]]++;
            for(int v = 0; v <= n; v++) 
                if(cap[u][v] || cap[v][u]) {
                if(d[v] == -1) d[v] = d[u] + 1, q.push(v);
            }
        }
    }
    int aug(int u, int inc) {
        int Inc = 0, mindis = n-1;
        if(u == t) return inc;
        for(int v = 0; v <= n; v++) 
            if(cap[u][v]) {
            int c = cap[u][v];
            if(c) {
                if(d[u] == d[v] + 1) {
                    int del = min(c, inc - Inc);
                    del = aug(v, del);
                    Inc += del;
                    cap[u][v] -= del;
                    cap[v][u] += del;
                    if(d[s] >= n) return Inc;
                    if(Inc == inc) return Inc;
                }
                mindis = min(mindis, d[v]);
            }
              
        }
        if(Inc == 0) {
            gap[d[u]]--;
            if(gap[d[u]] == 0) d[s] = n;
            d[u] = mindis+1;
            gap[d[u]]++;
        }
        return Inc;
    }
    int Maxflow(int _s, int _t, int _n) {
        s = _s; t = _t; n = _n;
        int ret = 0;
        init();
        while(d[s] < n) ret += aug(s, INF);
        return ret;
    }
} ;
ISAP <MAXN+10> sap;
bool check(int k) {
    sap.init(T);
    int N = 0;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            sap.cap[i][n+j] = A[i][j];
    for(int i = 0; i < n; i++) sap.cap[i][2*n] = (s1[i] == 'Y');
    for(int i = 0; i < n; i++) if(fa[i] == -1) sap.cap[S][i] = 1;
    for(int i = 0; i < n; i++) sap.cap[n+i][T] = k;
    sap.cap[n*2][T] = INF;
    for(int i = 0; i < n; i++) {
        if(fa[i] == -1) N++;
        else if(--sap.cap[n+fa[i]][T] < 0) return false;
    }
    int ans = sap.Maxflow(S, T, T+1);
    return ans == N;
}
int main() {
    memset(fa, -1, sizeof(fa));
    SF("%d", &n);
    S = 2*n+1; T = S+1;
    SF("%s", s1);
    for(int i = 0; i < n; i++) {
        SF("%s", s);
        for(int j = 0; j < n; j++) A[i][j] = s[j] == 'Y';
    }
    int L = 0, R = n;
    while(L < R) {
        int mid = (L + R) >> 1;
        if(check(mid)) R = mid;
        else L = mid+1;
    }
    for(int u = 0; u < n; u++) {
        do {
            fa[u]++;
        }while((fa[u] < n && !A[u][fa[u]]) || !check(L)) ;
        if(u != 0) putchar(' ');
        PF("%d", fa[u]);
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值