HDU3605Escape(最大流ISAP+状态压缩优化点的个数)

Escape

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 9652    Accepted Submission(s): 2295


Problem Description
2012 If this is the end of the world how to do? I do not know how. But now scientists have found that some stars, who can live, but some people do not fit to live some of the planet. Now scientists want your help, is to determine what all of people can live in these planets.
 

Input
More set of test data, the beginning of each data is n (1 <= n <= 100000), m (1 <= m <= 10) n indicate there n people on the earth, m representatives m planet, planet and people labels are from 0. Here are n lines, each line represents a suitable living conditions of people, each row has m digits, the ith digits is 1, said that a person is fit to live in the ith-planet, or is 0 for this person is not suitable for living in the ith planet.
The last line has m digits, the ith digit ai indicates the ith planet can contain ai people most..
0 <= ai <= 100000
 

Output
Determine whether all people can live up to these stars
If you can output YES, otherwise output NO.
 

Sample Input
  
  
1 1 1 1 2 2 1 0 1 0 1 1
 

Sample Output
  
  
YES NO
 

Source

2010 ACM-ICPC Multi-University Training Contest(17)——Host by ZSTU


题意:现在地球有n(<=100w)人数要去m(<=10)个其他星球生存,接下来n行每行m列,如果第i 为 1 对应这个人可去星球 i ,第 i 个星球最多可容ai个人(最后一行给出m个数)。现在问是否所有的人都可以到其他星球生存。

解题:最大流SAP。建图:因为人数多,而去星球的状态少最多是1024种,所以可以把每种状态看作是点,源点与每种状态连接一条边,容量为当前状态的人数,再每种状态与相应的星球连边,边容为当前状态的人数。再每个星球与汇点连边,边容为星球能容纳的人数。

ISAP算法还不懂的戳这里:http://www.renfei.org/blog/isap.html

最大流ISAP解法:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define PI acos(-1.0)
#define eps 1e-8
#define ll long long
#define MEM(a, b) memset(a, b, sizeof(a))
#define pb push_back
#define mp make_pair
#define MII map<int,int>::iterator
#define MLL map<LL,LL>::iterator
#define pii pair<int,int>
#define SI set<int>::iterator
#define SL set<LL>::iterator
#define dug printf("bug-------bug-------bug\n");
using namespace std;
const int MAXN = 1030;
const int MAXM = 14000;
const int INF = 1 << 30;
int head[MAXN], eid;
int d[MAXN], cur[MAXN], pre[MAXN], gap[MAXN];
int source, sink, nv;

inline void init()
{
    eid = 0;
    memset(head, -1, sizeof(head));
}
struct Edge
{
    int to, cap, nxt;
    Edge(){}
    Edge(int t, int c, int nx):to(t), cap(c), nxt(nx){}
}edge[MAXM];
void AddEdge(int u, int v, int c)
{
    edge[eid].to = v;
    edge[eid].cap = c;
    edge[eid].nxt = head[u];
    head[u] = eid++;

    edge[eid].to = u;
    edge[eid].cap = 0;
    edge[eid].nxt = head[v];
    head[v] = eid++;
}
void rev_bfs()
{
    memset(gap, 0, sizeof(gap));
    memset(d, -1, sizeof(d));
    d[sink] = 0;
    queue<int> q;
    q.push(sink);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int i = head[u]; i != -1; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if(d[v] != -1)
                continue;
            d[v] = d[u] + 1;
            q.push(v);
            gap[d[v]]++;
        }
    }
}
int ISAP()
{
    memcpy(cur, head, sizeof(cur));//复制,当前弧优化
    rev_bfs();
    int flow = 0, u = pre[source] = source;
    while(d[sink] < nv)
    {
        if(u == sink)//如果已经找到了一条增广路,则沿着增广路修改流量
        {
            int f = INF, neck;
            for(int i = source; i != sink; i = edge[cur[i]].to)
                if(f > edge[cur[i]].cap)
                {
                    f = edge[cur[i]].cap;//不断更新需要减少的流量
                    neck = i;//记录回退点,目的是为了不用再回到起点重新找
                }
            for(int i = source; i != sink; i = edge[cur[i]].to)//修改流量
            {
                edge[cur[i]].cap -= f;
                edge[cur[i]^1].cap += f;
            }
            flow += f;//更新
            u = neck;//回退
        }
        int i;
        for(i = cur[u]; i != -1; i = edge[i].nxt)
            if(d[edge[i].to] + 1 == d[u] && edge[i].cap)
                break;
        if(i != -1)
        {
            cur[u] = i;//如果存在可行增广路,更新
            pre[edge[i].to] = u;//修改当前弧
            u = edge[i].to;
        }
        else//否则回退,重新找增广路
        {
            if((--gap[d[u]]) == 0)//GAP间隙优化,如果出现断层,可以知道一定不会再有增广路了
                break;
            int mind = nv;
            for(i = head[u]; i != -1; i = edge[i].nxt)
            {
                if(edge[i].cap && mind > d[edge[i].to])//寻找可以增广的最小标号
                {
                    cur[u] = i;//修改当前弧
                    mind = d[edge[i].to];
                }
            }
            d[u] = mind + 1;
            gap[d[u]]++;
            u = pre[u];
        }
    }
    return flow;
}
int main()
{
    int n, m, s, t, ans, k[1030];
    while(scanf("%d%d", &n, &m) != EOF)
    {
        memset(k, 0, sizeof(k));
        int id = 0;
        for(int i = 0; i < n; i++)
        {
            int state = 0, choose;
            for(int j = 0; j < m; j++)
            {
                scanf("%d", &choose);
                if(choose)
                    state |= 1 << j;
            }
            if(k[state] == 0 && state)
                id++;
            k[state]++;
        }
        ans = n;
        init();

        n = id;
        source = 0, sink = n + m + 1, nv = n + m + 1;
        for(int i = (1<<m)-1; i > 0; i--)
        {
            if(k[i] > 0)
            {
                AddEdge(source, id, k[i]);
                for(int j = 0; (1<<j) <= i; j++)
                    if(i&(1<<j))
                        AddEdge(id, j+n+1, k[i]);
                id--;
            }
        }
        for(int j = 0; j < m; j++)
        {
            int star_live;
            scanf("%d", &star_live);
            if(star_live)
                AddEdge(j+n+1, sink, star_live);
        }
        ans -= ISAP();
        if(ans)
            puts("NO");
        else
            puts("YES");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值