P1345 [USACO5.4]奶牛的电信(点拆边 + 网络最小割)

题目描述
农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流。这些机器用如下的方式发送电邮:如果存在一个由c台电脑组成的序列a1,a2,…,a©,且a1与a2相连,a2与a3相连,等等,那么电脑a1和a©就可以互发电邮。

很不幸,有时候奶牛会不小心踩到电脑上,农夫约翰的车也可能碾过电脑,这台倒霉的电脑就会坏掉。这意味着这台电脑不能再发送电邮了,于是与这台电脑相关的连接也就不可用了。

有两头奶牛就想:如果我们两个不能互发电邮,至少需要坏掉多少台电脑呢?请编写一个程序为她们计算这个最小值。

以如下网络为例:

1* / 3 - 2*

这张图画的是有2条连接的3台电脑。我们想要在电脑1和2之间传送信息。电脑1与3、2与3直接连通。如果电脑3坏了,电脑1与2便不能互发信息了。

输入格式 第一行
四个由空格分隔的整数:N,M,c1,c2.N是电脑总数(1<=N<=100),电脑由1到N编号。M是电脑之间连接的总数(1<=M<=600)。最后的两个整数c1和c2是上述两头奶牛使用的电脑编号。连接没有重复且均为双向的(即如果c1与c2相连,那么c2与c1也相连)。两台电脑之间至多有一条连接。电脑c1和c2不会直接相连。

第2到M+1行 接下来的M行中,每行包含两台直接相连的电脑的编号。

输出格式 一个整数表示使电脑c1和c2不能互相通信需要坏掉的电脑数目的最小值。

输入输出样例

输入 #1 复制
3 2 1 2
1 3
2 3
输出 #1 复制
1

思路

题意:这一题是问cow需要最少破话多少台电脑才能 才能使位于起点和终点位置的 cow 不能通信,也就是说这一题是让拆的是,而不是让拆的边,我们不能够直接使用,这一题如果是让拆的是边的话,那就很简单,因为不能够通信,就意味着 在拆边之后,图的联通性就没了,图被分割成两部分,那么我们所要求的是最小割。。。。。这一题是 割点 ,当我们在破坏掉一个u 之后(假设这一条边为 u——v),我们不能够影响原来 v点的通与其他点的通信(除了u点),这一题如果我们能够把个 割点——> 转化割边 就好解决了
思路:我们可以考虑把 一个点(假定编号为 i)拆成两个点,这两个点的编号是 :i , i + n,在 i 与 i+1 点之间有一条权值为 1 指向 i+1 点的边(i+1这个点 和这个边是我们要用 Add函数添加进去的) ,还有一点要明白,对于题上给的边,所有与原来 i 所连的点 进入i的边 还是i ,而原来 从 i 点出去的边要变成 从 i + 1 点过出去的边,一定是可以双向通行,而且可以无线通信(所以题目上给的边就是 INF),i 点拆分后的结果
在这里插入图片描述
当我们把所有的点之后,再把 题目上给的边我们就得到了最终的图:
在这里插入图片描述
上图⬆️中 的 红线:拆点之间的边权为 1 的线,黑线是题目给的边权值为 INF
最后最后:我么要特殊考虑:源点s 与汇点e , 我们要明白 原点可以通过与 源点相连的边无限发送消息,而不是一次,所以我们 这个时候 所用的原点就变成了 s + n(这个是因为 拆点造成的),而汇点可以通过入边无相接受消息,所以 入边 链接的还是 e (即所用的汇点e不变)

题解(Dinic)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

#define INF 0x3f3f3f3f
const int maxn = 205;
const int maxm = 2005;
struct Edge
{
    int v,w,next;
} edge[2*maxm];
int head[maxm],cur[maxm];
int deep[maxm];
int use[maxm];
int n,m,s,e;

int k = -1;
void Add(int u, int v, int w)
{
    edge[++ k] = (Edge){ v, w, head[u]}; head[u] = k;
    edge[++ k] = (Edge){ u, 0, head[v]}; head[v] = k;
}

//来分层用的
bool bfs(int s, int e)
{
    for(int i = 0; i <= 2*n; i ++)
        cur[i] = head[i], deep[i] = INF, use[i] = 0;

    deep[s] = 0;
    queue<int> q;
    q.push(s);
    int u,v;
    while(! q.empty())
    {
        u = q.front(); q.pop();
        use[u] = 0;

        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            if(edge[i].w && deep[v] == INF)
            {
                deep[v] = deep[u] + 1;
                if(! use[v])
                {
                    q.push(v);
                    use[v] = 1;
                }
            }
        }
    }
    if(deep[e] == INF)
        return false;
    return true;
}

int dfs(int now, int e, int limit)
{
    if(! limit || now == e) return limit;
    int flow = 0,f;
    for(int i = cur[now]; i != -1; i = edge[i].next)
    {
        if(deep[edge[i].v] == deep[now] + 1 && (f = dfs(edge[i].v, e, min(limit, edge[i].w))))
        {
            cur[now] = i;   //当前弧优化
            flow += f;
            limit -= f;
            edge[i].w -= f;
            edge[i^1].w += f;
            if(! limit)
                break;
        }
    }
    return flow;
}

int Dinic(int s, int e)
{
    int mx_flw = 0;
    while(bfs(s, e))
        mx_flw += dfs(s, e, INF);
    return mx_flw;
}


void init()
{
    k = -1;
    memset(head, -1, sizeof(head));
}

int main()
{
    ios::sync_with_stdio(false);
    //freopen("T.txt","r",stdin);
    cin >> n >> m >> s >> e;
    s += n;
    init();
    //拆点操作
    for(int i = 1; i <= n; i ++)
        Add(i, i + n, 1);
    int u, v;
    for(int i = 1; i <= m; i ++)
        cin >> u >> v, Add(u + n, v, INF), Add(v + n, u, INF);

    cout << Dinic(s, e) << endl;

    return 0;
}

发布了136 篇原创文章 · 获赞 218 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/104605909