zoukankan      html  css  js  c++  java
  • 【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
  • 相关阅读:
    解析大型.NET ERP系统 权限模块设计与实现
    Enterprise Solution 开源项目资源汇总 Visual Studio Online 源代码托管 企业管理软件开发框架
    解析大型.NET ERP系统 单据编码功能实现
    解析大型.NET ERP系统 单据标准(新增,修改,删除,复制,打印)功能程序设计
    Windows 10 部署Enterprise Solution 5.5
    解析大型.NET ERP系统 设计异常处理模块
    解析大型.NET ERP系统 业务逻辑设计与实现
    解析大型.NET ERP系统 多国语言实现
    Enterprise Solution 管理软件开发框架流程实战
    解析大型.NET ERP系统 数据审计功能
  • 原文地址:https://www.cnblogs.com/LQLlulu/p/9307301.html
Copyright © 2011-2022 走看看