HDU 3605 Escape【最大流】

意:有n个人,m个星球,要搬家,一些人有特定喜欢的星球,让尽量多的人到星球。

输入n,m

输入n*m矩阵表示 i 人是否喜欢 j 星球

输入m个数字表示星球对人的容量


直接建图会超时,然后我们发现m最大是10,所以每个人对星球的选择最多为2^10种,所以肯定有很多人的选择是一样的,所以建图的人点改为选择的情况。

Dinic TLE,贴个SAP模板就过了。。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false)
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define NMAX 10002
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
const int inf = 0x3f3f3f3f;
const int maxn = 1044; //2^10+10+2=1036
const int maxm = 12000;     //2^10+2^10*10+10=11274
struct Edge {
    int to, next, cap, flow;
} edge[maxm*2]; //注意是MAXM
int tol;
int head[maxn];
int gap[maxn], dep[maxn], cur[maxn];
void init() {
    tol = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w, int rw = 0) {
    edge[tol].to = v;
    edge[tol].cap = w;
    edge[tol].flow = 0;
    edge[tol].next = head[u];
    head[u] = tol++;
    edge[tol].to = u;
    edge[tol].cap = rw;
    edge[tol].flow = 0;
    edge[tol].next = head[v];
    head[v] = tol++;
}
int Q[maxn];
void BFS(int start, int end) {
    memset(dep, -1, sizeof(dep));
    memset(gap, 0, sizeof(gap));
    gap[0] = 1;
    int front = 0, rear = 0;
    dep[end] = 0;
    Q[rear++] = end;
    while (front != rear) {
        int u = Q[front++];
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (dep[v] != -1) {
                continue;
            }
            Q[rear++] = v;
            dep[v] = dep[u] + 1;
            gap[dep[v]]++;
        }
    }
}
int S[maxn];
int sap(int start, int end, int N) {
    BFS(start, end);
    memcpy(cur, head, sizeof(head));
    int top = 0;
    int u = start;
    int ans = 0;
    while (dep[start] < N) {
        if (u == end) {
            int Min = inf;
            int inser;
            for (int i = 0; i < top; i++)
                if (Min > edge[S[i]].cap - edge[S[i]].flow) {
                    Min = edge[S[i]].cap - edge[S[i]].flow;
                    inser = i;
                }
            for (int i = 0; i < top; i++) {
                edge[S[i]].flow += Min;
                edge[S[i] ^ 1].flow -= Min;
            }
            ans += Min;
            top = inser;
            u = edge[S[top] ^ 1].to;
            continue;
        }
        bool flag = false;
        int v;
        for (int i = cur[u]; i != -1; i = edge[i].next) {
            v = edge[i].to;
            if (edge[i].cap - edge[i].flow && dep[v] + 1 == dep[u]) {
                flag = true;
                cur[u] = i;
                break;
            }
        }
        if (flag) {
            S[top++] = cur[u];
            u = v;
            continue;
        }
        int Min = N;
        for (int i = head[u]; i != -1; i = edge[i].next)
            if (edge[i].cap - edge[i].flow && dep[edge[i].to] < Min) {
                Min = dep[edge[i].to];
                cur[u] = i;
            }
        gap[dep[u]]--;
        if (!gap[dep[u]]) {
            return ans;
        }
        dep[u] = Min + 1;
        gap[dep[u]]++;
        if (u != start) {
            u = edge[S[--top] ^ 1].to;
        }
    }
    return ans;
}
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int a[maxn];
int main() {
    int n, m;
//        RE
    while (~scanf("%d%d", &n, &m)) {
        CLR(a, 0);
        init();
        int ss = 0, tt = (1 << m) + m + 1; //人:1~1<<m 星球:(1<<m)+1~(1<<m)+m 终点:(1<<m)+m+1
        FOR(i, 1, n) {
            int tmp = 0;
            FOR(j, 0, m - 1) {
                int w = read();
                if (w) {
                    tmp |= (1 << (j));
                }
            }
            a[tmp]++;
        }
        FOR(j, 1, m) {
            int w = read();
            addedge((1 << m) + j, tt, w); //星球与终点
        }
        for (int i = 0; i < (1 << m); i++) { //遍历人的情况,0到(1<<m)-1
            if (a[i]) {
                addedge(ss, 1 + i, a[i]);     //起点与人
                for (int j = 0; j < m; j++) {
                    if (i & (1 << j)) { //如果i里的j位是1就说明与j星球有边
                        addedge(i + 1, (1 << m) + j + 1, a[i]); //人和星球建边
                    }
                }
            }
        }
        if (sap(ss,tt,tt) >= n) {
            puts("YES");
        } else {
            puts("NO");
        }
    }
    return 0;
}

TLE

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false)
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define NMAX 10002
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
const int inf=0x3f3f3f3f;
const int maxn=1044; //2^10+10+2=1036
const int maxm=12000;       //2^10+2^10*10+10=11274
int head[maxn],dis[maxn],cnt,ss,tt;
struct Edge
{
    int v,w,next;
}edge[maxm*2];
int a[maxn];
void add(int u,int v,int w)
{
    edge[cnt].v=v,edge[cnt].w=w,edge[cnt].next=head[u],head[u]=cnt++;
    edge[cnt].v=u,edge[cnt].w=0,edge[cnt].next=head[v],head[v]=cnt++;
}
void init()
{
    CLR(head,-1);
    cnt=0;
}
int bfs()
{
    queue<int>q;
    q.push(ss);
    CLR(dis,-1);
    dis[ss]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(edge[i].w && dis[v]<0)
            {
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    return dis[tt]>0;
}
int dfs(int u,int tmin)
{
    int flow;
    if(u==tt)   return tmin;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(edge[i].w &&
            (dis[v]==dis[u]+1) &&
                (flow=dfs(v,min(tmin,edge[i].w)))
        ){
            edge[i].w-=flow;
            edge[i^1].w+=flow;
            return flow;
        }
    }
//    dis[u]=-1;
    return 0;
}
int maxFlow()
{
    int ans=0,tans=0;
    while(bfs())
    {
        while(tans=dfs(ss,inf))
            ans+=tans;
    }
    return ans;
}
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    int n,m;
//    RE
    while(~scanf("%d%d",&n,&m))
    {
        CLR(a,0);
        init();
        ss=0,tt=(1<<m)+m+1; //人:1~1<<m 星球:(1<<m)+1~(1<<m)+m 终点:(1<<m)+m+1
        FOR(i,1,n)
        {
            int tmp=0;
            FOR(j,0,m-1)
            {
                int w=read();
                if(w)
                    tmp|=(1<<(j));
            }
            a[tmp]++;
        }
        FOR(j,1,m)
        {
            int w=read();
            add((1<<m)+j,tt,w);     //星球与终点
        }
        for(int i=0;i<(1<<m);i++)  //遍历人的情况,0到(1<<m)-1
        {
            if(a[i])
            {
                add(ss,1+i,a[i]);         //起点与人
                for(int j=0;j<m;j++)
                {
                    if(i&(1<<j))        //如果i里的j位是1就说明与j星球有边
                    {
                        add(i+1,(1<<m)+j+1,a[i]);    //人和星球建边
                    }
                }
            }
        }
        if(maxFlow()>=n)
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值