• 【UVA11419 训练指南】我是SAM 【二分图最小覆盖,最小割】


    题意

      给出一个R*C大小的网格,网格上面放了一些目标。可以在网格外发射子弹,子弹会沿着垂直或者水平方向飞行,并且打掉飞行路径上的所有目标。你的任务是计算最少需要多少子弹,各从哪些位置发射,才能把所有目标全部打掉。

    分析

    啊!原来这个模型叫 最小覆盖模型啊!难道不是最小割直接做嘛??

    二分图最小覆盖:既选择尽量少的点,使得每条边至少有一个端点被选中。可以证明,最小覆盖数等于最大匹配数。

    本题的建模方法:

     将每一行看作一个X结点,每一列看作一个Y结点,每个目标对应一条边。这样,子弹打掉左右的目标意味着每条边至少有一个节点被选中。

     好吧,我还是说一下我最小割的理解吧。建模一开始还是一样的,每一行为X结点,每一列为Y结点,每个目标对应一条从X到Y的一条边。然后从s向每个X结点连一条容量为1的边。然后每个Y结点都向t结点连一条容量为1的边。求最小割的时候一定是个割从s连出的边或者连向t的边。而如果割s->u这条边,那么就代表这一行打子弹,割v->t也同理。

     如果只是输出这个最小的子弹数,那么到这里就结束了,是个非常简单的最小割。但是这个题要输出方案!

     按照最小割来说的话,我们要知道,我们割的是哪一条边。一开始我想当然以为就是输出满流的边,但是很显然不对啊!

     然后,然后,然后我就去可耻的百度了。

      哪些边是最小割的边呢?我们知道,跑完最小割是以后是把所有的点都分为X阵营和Y阵营。那么连接着两个阵营的边就是最小割的边。那么我们只要找出哪些点是X哪些点是Y就可以了。怎么找呢?我们跑完dinic以后,是得到了一个不存在增广路的残量网络的。此时X和Y是分开的,那么只要从s结点开始顺着残量网络的边(包括反向边)跑一个DFS,只要能跑到的点都是X阵营。

     具体看下面的代码吧~

     

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #include <iostream>
      5 #include <queue>
      6 
      7 using namespace std;
      8 const int maxn=1000000+10;
      9 const int INF=2147480000;
     10 
     11 struct Dinic{
     12     int head[maxn],Next[3*maxn],to[3*maxn],cap[3*maxn],flow[3*maxn];
     13     int sz,n,m,s,t;
     14     bool vis[maxn];
     15     int cur[maxn],d[maxn];
     16     void init(int n){
     17         this->n=n;
     18         memset(head,-1,sizeof(head));
     19         this->sz=-1;
     20     }
     21     void add_edge(int a,int b,int c){
     22         ++sz;
     23         to[sz]=b;
     24         cap[sz]=c;flow[sz]=0;
     25         Next[sz]=head[a];head[a]=sz;
     26         ++sz;
     27         to[sz]=a;
     28         cap[sz]=c;flow[sz]=c;
     29         Next[sz]=head[b];head[b]=sz;
     30     }
     31     bool BFS(){
     32         memset(vis,0,sizeof(vis));
     33         queue<int>Q;
     34         vis[s]=1;
     35         d[s]=0;
     36         Q.push(s);
     37         while(!Q.empty()){
     38             int u=Q.front();Q.pop();
     39             for(int i=head[u];i!=-1;i=Next[i]){
     40                 int v=to[i];
     41                 if(!vis[v]&&cap[i]>flow[i]){
     42                     vis[v]=1;
     43                     d[v]=d[u]+1;
     44                     Q.push(v);
     45                 }
     46             }
     47         }
     48         return vis[t];
     49    }
     50     int DFS(int x,int a){
     51         if(x==t||a==0)return a;
     52         int Flow=0,f;
     53         for(int& i=cur[x];i!=-1;i=Next[i]){
     54             int v=to[i];
     55             if(d[v]==d[x]+1&&(f=DFS(v,min(a,cap[i]-flow[i])))>0){
     56                 Flow+=f;
     57                 flow[i]+=f;
     58                 flow[i^1]-=f;
     59                 a-=f;
     60                 if(a==0)break;
     61             }
     62         }
     63         return Flow;
     64     }
     65     int Maxflow(int s,int t){
     66         this->s=s,this->t=t;
     67         int Flow=0;
     68         while(BFS()){
     69             for(int i=0;i<=n;i++)
     70              cur[i]=head[i];
     71 
     72             Flow+=DFS(s,INF);
     73         }
     74         return Flow;
     75     }
     76 }dinic;
     77 int R,C,N;
     78 int foot[maxn];
     79 void get_ans(int u){
     80     foot[u]=1;
     81     for(int i=dinic.head[u];i!=-1;i=dinic.Next[i]){
     82         int v=dinic.to[i];
     83         if(!foot[v]&&dinic.cap[i]>dinic.flow[i])
     84             get_ans(v);
     85     }
     86     return;
     87 }
     88 
     89 int main(){
     90     while(scanf("%d%d%d",&R,&C,&N)!=EOF&&(R||C||N)){
     91         memset(foot,0,sizeof(foot));
     92         dinic.init(R+C+5);
     93         int r,c;
     94         for(int i=1;i<=N;i++){
     95             scanf("%d%d",&r,&c);
     96             dinic.add_edge(r,c+R,1);
     97         }
     98         for(int i=1;i<=R;i++)
     99             dinic.add_edge(0,i,1);
    100         for(int i=1;i<=C;i++)
    101             dinic.add_edge(i+R,C+R+1,1);
    102         int ans=dinic.Maxflow(0,C+R+1);
    103         printf("%d ",ans);
    104         get_ans(0);
    105         for(int i=1;i<=R;i++){
    106             if(!foot[i]){
    107                 printf("r%d ",i);
    108             }
    109         }
    110         for(int i=1;i<=C;i++){
    111             int u=i+R;
    112             if(foot[u]){
    113                 printf("c%d ",i);
    114             }
    115         }
    116         printf("
    ");
    117     }
    118 return 0;
    119 }
    View Code
  • 相关阅读:
    docker删除所有服务service,停止并删除所有容器container
    harbor
    yml文件
    linux 上安装portainer.io
    凤凰之谜 1/4 潜行者
    凤凰之谜 4/4 猎人
    凤凰之谜 3/4 德鲁伊 迷宫
    Dijkstra最短路径算法
    LeetCode 到底怎么刷?GitHub 上多位大厂程序员亲测的高效刷题方式
    How do I run a Python script from C#?
  • 原文地址:https://www.cnblogs.com/LQLlulu/p/9307301.html
Copyright © 2020-2023  润新知