• hdu2732 最大流+拆点


    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2732

    题目给定一个场景,有n*m个方格,每个方格代表一个柱子,一个柱子可以承受不同次数的跳跃,开始时图中给定一些地方有蜥蜴,并且给定蜥蜴最多跳跃的步长,只要跳到方格之外就能安全,而且每只蜥蜴不能在同一个地方重合,每次蜥蜴跳离一个地方这个地方的柱子就的承受次数就会减一,问最终会有多少只蜥蜴不能跳出迷宫。

    这个问题可以这样思考,每次蜥蜴跳出一个位置之后这个位置的“资源”就会减少1,而这个减少之后的“资源”不可以再利用,而且涉及到点之间的转移,这在图论中和最大流很相似,所以我们想到可不可以将点转化成状态,地图中的每一个位置(i,j)(0<=i,j<n)可以通过i*m+j+1转化成[1,n*m]闭区间之内的位置,所以我们可以把这个设为他的id,可以将点位置拆开成两个,一个是id,表示没有转移时的状态,另一个点是id+n*m,表示这个点拆开之后的另一个状态。设置一个超级源和一个超级汇。

    转换策略如下:

    ①、如果一个点位置(i,j)值num大于0,那么可以连边(id,id+n*m,num),表示最多有num次“流”在这个位置转换,num个用完也就代表不能发生转换,也就是这个位置不能站蜥蜴了。

    ②、如果一个位置通过跳跃d可以到达外围就设置边(id,T,inf),就是把这个位置和超级汇连接起来,并且不限制从这里通过的蜥蜴的数量。

    ③、如果一个位置有蜥蜴,我们可以把这个位置和超级源连接在一起,也就是(S,id,1)1表示在这个位置有一只蜥蜴出发。就相当于把蜥蜴放在一个源点,蜥蜴数量便是源点流量。

    ④、两个位置之间的距离(横纵坐标绝对值之差)如果小于d那就可以转换,注意连的边是(id+n*m,id2,1),因为在他跳出当前位置时必须当前位置的承受容量减一,所以必须重跳完之后的状态转移。

    基本思想就是拆点。需要慎重思考转移的方式。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 typedef unsigned int ui;
      4 typedef long long ll;
      5 typedef unsigned long long ull;
      6 #define pf printf
      7 #define mem(a,b) memset(a,b,sizeof(a))
      8 #define prime1 1e9+7
      9 #define prime2 1e9+9
     10 #define pi 3.14159265
     11 #define lson l,mid,rt<<1
     12 #define rson mid+1,r,rt<<1|1
     13 #define scand(x) scanf("%llf",&x) 
     14 #define f(i,a,b) for(int i=a;i<=b;i++)
     15 #define scan(a) scanf("%d",&a)
     16 #define mp(a,b) make_pair((a),(b))
     17 #define P pair<int,int>
     18 #define dbg(args) cout<<#args<<":"<<args<<endl;
     19 #define inf 0x7ffffff
     20 inline int read(){
     21     int ans=0,w=1;
     22     char ch=getchar();
     23     while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
     24     while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
     25     return ans*w;
     26 }
     27 const int maxn=1010;
     28 const int maxm=100010;
     29 struct flow{
     30     int s,t;
     31     int head[maxn],nxt[maxm],d[maxn],cur[maxn];//cur当前弧优化 
     32     bool vis[maxn];
     33     struct node{
     34         int u,v,w;
     35     }p[maxm];
     36     int e;
     37     void init()
     38     {
     39         e=0;
     40         mem(head,-1);
     41         mem(nxt,-1);
     42     }
     43     void addedge(int u,int v,int w)//顺带添加反向边 
     44     {
     45         p[e].u=u;
     46         p[e].v=v;
     47         p[e].w=w;
     48         nxt[e]=head[u];
     49         head[u]=e++;
     50         p[e].u=v;
     51         p[e].v=u;
     52         p[e].w=0;
     53         nxt[e]=head[v];
     54         head[v]=e++; 
     55     }
     56     bool bfs(int src,int sink)//确定bfs序 
     57     {
     58         mem(vis,0);
     59         mem(d,0);
     60         d[src]=1;
     61         vis[src]=1;
     62         queue<int> q;
     63         q.push(src);
     64         while(!q.empty())
     65         {
     66             int cur=q.front();
     67             q.pop();
     68             for(int i=head[cur];~i;i=nxt[i])
     69             {
     70                 int v=p[i].v;
     71                 if(!vis[v]&&p[i].w)//确定这个点没有被标号,并且不是反向边 
     72                 {
     73                     vis[v]=1;
     74                     d[v]=d[cur]+1;
     75                     q.push(v);
     76                 }
     77             }
     78         }
     79         if(d[sink])return true;
     80         return false;
     81     }
     82     int dfs(int s,int flow)
     83     {
     84         if(s==t)return flow;
     85         int used=0;
     86         for(int& i=cur[s];~i;i=nxt[i])
     87         {
     88             int v=p[i].v,w=p[i].w;
     89             if(d[v]==d[s]+1&&w>0)//根据Dinic算法的思想,只能走正向的、bfs序大一的边 
     90             {
     91                 int tmp=dfs(v,min(flow-used,w));
     92                 if(tmp>0)
     93                 {
     94                     p[i].w-=tmp;//更新正向边的流量以及反向边的流量, 
     95                     p[i^1].w+=tmp;//正向边是偶数,它对应的反向边就是正向边+1
     96                     used+=tmp;//从一个点出发最多的流量是flow,用掉的流量需要更新
     97                     if(used==flow)break; 
     98                  } 
     99             }
    100         }
    101         if(!used)d[s]=0;//从该点出发的流不能被使用,所以这个点在这次搜索中被丢弃 
    102         return used;
    103     }
    104     int dinic()
    105     {
    106         int ans=0;
    107         while(bfs(s,t))
    108         {
    109             memcpy(cur,head,sizeof(head));
    110             ans+=dfs(s,inf);
    111         }
    112         return ans;
    113     }
    114 }a;
    115 int d;
    116 int t;
    117 char s[50];
    118 int main()
    119 {
    120     //freopen("input.txt","r",stdin);
    121     //freopen("output.txt","w",stdout);
    122     std::ios::sync_with_stdio(false);
    123     t=read();
    124     int n,m;    
    125     int kase=0;
    126     while(t--)
    127     {
    128         a.init();
    129         int cnt=0;
    130         n=read(),d=read();
    131         f(i,0,n-1)
    132         {
    133             scanf(" %s",s);
    134             if(i==0)
    135             {
    136                 m=strlen(s);
    137 //                a.n=2*n*m+2;//最大流中点的数量设置 
    138                 a.s=0;
    139                 a.t=2*n*m+1;//设置超级源与超级汇 
    140             }
    141             f(j,0,m-1)
    142             {
    143                 int num=s[j]-'0';
    144                 if(num>0)//将大于零的格子拆开成两个格子,并连线, 
    145                 {
    146                     int id=i*m+j+1;
    147                     a.addedge(id,id+n*m,num);
    148                     if(i-d<0||i+d>=n||j-d<0||j+d>=m)
    149                     {
    150                         a.addedge(id+n*m,a.t,inf);//可以到达安全地点,且流的大小不限制 
    151                     }
    152                     else
    153                     {
    154                         f(k,0,n-1)//由于图的大小不大,所以可以枚举每一个位置检查是否可行 
    155                             f(l,0,m-1)
    156                             {
    157                                 int id2=k*m+l+1;
    158                                 if(id==id2)continue;
    159                                 if(abs(i-k)+abs(j-l)<=d)
    160                                     a.addedge(id+n*m,id2,inf);//从id位置能够跳到id2位置,但是记住一定是在id位置少1之后的结点上 
    161                             }
    162                      } 
    163                 }
    164             }
    165         }    
    166             f(i,0,n-1)
    167             {
    168                 scanf(" %s",s);
    169                 f(j,0,m-1)
    170                 {
    171                     if(s[j]=='L')//记录蜥蜴的数量并与超级源连线, 
    172                     {
    173                         cnt++;
    174                         int id=i*m+j+1;
    175                         a.addedge(a.s,id,1); 
    176                     }
    177                 }
    178             }
    179             int ans=cnt-a.dinic();
    180             if(ans==0)pf("Case #%d: no lizard was left behind.
    ",++kase);
    181             else if(ans==1)pf("Case #%d: 1 lizard was left behind.
    ",++kase);
    182             else pf("Case #%d: %d lizards were left behind.
    ",++kase,ans);        
    183         
    184     }
    185 } 
    每一个不曾起舞的日子,都是对生命的辜负。
  • 相关阅读:
    jquery开发之第一个程序
    结构体大小求值
    SpringMVC 理论与有用技术(一) 简单、有用、易懂的几个实例
    北极的夜空
    Assignment (HDU 2853 最大权匹配KM)
    让linux history命令显示命令的运行时间、在哪个机器运行的这个命令
    [0day]基础工具学习
    Matlab adaptive quadrature
    计蒜之道 初赛 第三场 题解 Manacher o(n)求最长公共回文串 线段树
    辛星跟您解析在CSS面包屑中三角形的定位问题
  • 原文地址:https://www.cnblogs.com/randy-lo/p/12594794.html
Copyright © 2020-2023  润新知