ZOJ 2788 最小割

虽然是自己做出来的,不过为什么这么构图感觉说不清,别人的解法写的挺好的.

解法:显然这是一个集合的分割问题,即求这样的一个割:使得终点房间与某些存在人的房间的一个分割,题中求的最少的人就是求解一个最小割。将问题转化为网络流求解。通过建立从超级源点到存在人的一些房间,那么从汇点反向遍历寻找这样的一个割。如果从源点到有人房间的边满流,那么反向遍历一定不会将这个节点划分到汇点集合里面去,如果该边不满流的话,如果划分到了汇点集合,则表明存在从源点到汇点的流量,于最大流相悖。因此所求既满足题意。

#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

typedef  long long LL;
const double PI = acos(-1.0);

template <class T> inline  T MAX(T a, T b){if (a > b) return a;return b;}
template <class T> inline  T MIN(T a, T b){if (a < b) return a;return b;}

const LL MOD = 1000000007LL;
const int dir[4][2] = {1, 0, -1, 0, 0, -1, 0, 1};
const int INF = 1000;


const int MAXN = 880 , MAXE = 44000;
struct Arclist{
	struct Edge{int v,cap,next;}E[MAXN+MAXE<<1];
	int head[MAXN],cur;
	void init(){memset(head, -1, sizeof(head));cur=0;}
	void add(int u,int v,int cap=1){
	    E[cur].v=v;E[cur].cap=cap;
	    E[cur].next=head[u];head[u]=cur++;
	    E[cur].v=u;E[cur].cap=0;
	    E[cur].next=head[v];head[v]=cur++;
	}
    int SAP(int S,int T,int N){
        int maxflow=0,pre[MAXN],dis[MAXN]={},gap[MAXN]={};
        int cur[MAXN];memcpy(cur, head, sizeof(head));
        gap[0]=N+1;++gap[dis[S]=1];
        for(int u=pre[S]=S;dis[S]<=N;++gap[++dis[u]],u=pre[u]){
            for(bool flag=true;flag;){flag=false;
                for(int&p=cur[u];~p;p=E[p].next){
                    if(!E[p].cap||dis[u]!=dis[E[p].v]+1)continue;
                    flag=true;pre[E[p].v]=u;u=E[p].v;
                    if(u==T){
                        int aug=INF;
                        for(int i=S;i!=T;i=E[cur[i]].v)
                        if (aug > E[cur[i]].cap)
                        {
                            u = i; aug = E[cur[i]].cap;
                        }
                        for(int i=S;i!=T;i=E[cur[i]].v){
                            E[cur[i]].cap-=aug;
                            E[cur[i]^1].cap+=aug;
                        }
                        maxflow+=aug;
                    }
                    break;
                }
            }
            if(--gap[dis[u]]==0)break;
            dis[u]=N;
            for(int p=head[u];~p;p=E[p].next)
				 if (E[p].cap && dis[u] > dis[E[p].v]) {dis[u] = dis[E[p].v]; cur[u] = p;}
        }
        return maxflow;
    }
}flow;


int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int flag, n, tag, i, j, k, u, v, w;
        flow.init();
        scanf("%d%d", &n, &tag);
        char str[10]; tag++;
        for (i = 0; i < n; ++i)
        {
            scanf("%s", str);
            scanf("%d", &k);
            if (str[0] == 'I')
            {
//                if (i + 1 == tag) flag = false;
                flow.add(0, i + 1, INF);
//                flow.add(i + 1, 0, 0);
            }
            for (j = 0; j < k; ++j)
            {
                scanf("%d", &v);
                flow.add(i + 1, v + 1, INF);
                flow.add(v + 1, i + 1);
            }
        }
//        if (!flag) {printf("PANIC ROOM BREACH\n"); continue;}
        int ans = flow.SAP(0, tag, n + 1);
        if (ans >= INF) printf("PANIC ROOM BREACH\n");
        else printf("%d\n", ans);
    }
    return 0;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值