zoukankan      html  css  js  c++  java
  • 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 } 
    每一个不曾起舞的日子,都是对生命的辜负。
  • 相关阅读:
    fullCalendar改造计划之带农历节气节假日的万年历(转)
    Linked List Cycle
    Remove Nth Node From End of List
    Binary Tree Inorder Traversal
    Unique Binary Search Trees
    Binary Tree Level Order Traversal
    Binary Tree Level Order Traversal II
    Plus One
    Remove Duplicates from Sorted List
    Merge Two Sorted Lists
  • 原文地址:https://www.cnblogs.com/randy-lo/p/12594794.html
Copyright © 2011-2022 走看看