hdoj 3605 Escape 【状态压缩构图 + 最大流】



Escape

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


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
 


少了一道剩题。。。

这道题:

时间限制1s -> 数据加强 -> 网上的AC代码TLE -> 我的代码也TLE -> 时间限制修改2s ->我以前的代码AC。

o(╯□╰)o


题意:有N个人要去M个星球避难,现在给你每个人适合居住的星球以及每个星球可以容纳的人数,问你这N个人能不能全部安全避难。


分析:由于N最大为100000,若直接建边的话,网络流的任何算法都承受不起这个时间复杂度。因为M个数最多为10,利用状态来表示的话最坏只有2^10 = 1024个点,我们可以利用状态压缩的方法来优化时间复杂度。


建图:设置超级源点source,超级汇点sink,并记录每个状态出现的次数

1,source向所有状态建边,容量为该状态出现的次数;

2,所有状态向该状态里面存在的星球建边,容量为无穷大,表示source的流量可以无限到该星球;

3,对流入sink的流量加限制,每个星球向sink建边,容量为星球可以容纳的人数。

最后跑一次最大流,看是否满流就可以了。


AC代码:


#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 200000+100
#define MAXM 1000000+100
#define INF 100000000
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
}edge[MAXM];
int head[MAXN], edgenum;
int cur[MAXN];
int dist[MAXN];
bool vis[MAXN];
int time[MAXN];//标记某个状态出现的次数
int N, M;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    edge[edgenum].from = u;
    edge[edgenum].to = v;
    edge[edgenum].cap = w;
    edge[edgenum].flow = 0;
    edge[edgenum].next = head[u];
    head[u] = edgenum++;
    edge[edgenum].from = v;
    edge[edgenum].to = u;
    edge[edgenum].cap = 0;
    edge[edgenum].flow = 0;
    edge[edgenum].next = head[v];
    head[v] = edgenum++;
}
void getMap()
{
    int a;
    memset(time, 0, sizeof(time));
    //状态最多有1024个  加上星球数目  最坏情况1024 + 10 个点
    //超级源点 1024 + 11  超级汇点 1024 + 12
    for(int i = 1; i <= N; i++)//每个人对应一种状态
    {
        int mark = 0;
        for(int j = 0; j < M; j++)
        {
            scanf("%d", &a);
            if(a) mark += 1<<j;//记录状态
        }
        time[mark]++;//状态出现次数加一
    }
    for(int i = 0; i < (1<<M); i++)//枚举所有状态 即 (2的M次方 - 1)个状态
    {
        if(time[i])//出现过
        {
            addEdge(1035, i, time[i]);//从超级源点引一条边 权值为该状态出现次数
            //和与该状态有关的星球 建边 权值为INF
            for(int j = 0; j < M; j++)//遍历所有星球  星球编号为1025 + j
                if(i & (1<<j)) addEdge(i, j + 1025, INF);
        }
    }
    for(int i = 0; i < M; i++)
    {
        scanf("%d", &a);
        addEdge(i + 1025, 1036, a);//每个星球 引一条到汇点的边 权值为星球的容量
    }
}
bool BFS(int sx, int ex)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[sx] = 0;
    vis[sx] = true;
    Q.push(sx);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow)
            {
                vis[E.to] = true;
                dist[E.to] = dist[u] + 1;
                Q.push(E.to);
                if(E.to == ex) return true;
            }
        }
    }
    return false;
}
int DFS(int x, int a, int ex)
{
    if(x == ex || a == 0) return a;
    int flow = 0, f;
    for(int &i= cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap - E.flow), ex)) > 0)
        {
            E.flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Maxflow(int sx, int ex)
{
    int flow = 0;
    while(BFS(sx, ex))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(sx, INF, ex);
    }
    return flow;
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        init();
        getMap();
        if(Maxflow(1035, 1036) == N)
        printf("YES\n");
        else
        printf("NO\n");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值