网络流强化-HDU2732

  第一次遇到加了“多余”的边会导致WA的——在我看来是很多余,见代码191行

  之后会思考为什么,想出来再更。

  

//http://www.renfei.org/blog/isap.html 带解释的
//https://www.cnblogs.com/bosswnx/p/10353301.html 形式和我的比较相近的
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define maxe 100024  //pay  双向边 一共10万条路 双向就是20万 反边就是40万
#define maxv 1024    //pay
#define maxn 25    //pay
#define sc scanf
#define pt printf
#define rep(i,a,b)  for(int i=(a);i<(b);++i)
const int inf = 0x3f3f3f3f;
int cg,sp,ins;  //cg change sp是总流量 ins是加速回溯点
int N,M,T,D ,s,t,delta;
int q[maxv],fro,rea;
typedef struct ed{
    int v,nxt,cap; //dis
}ed;
ed e[maxe];
int tot,head[maxv],cur[maxv],vis[maxv],bk[maxv],d[maxv],num[maxv]; //
int mi(int a,int b) {return a<b?a:b;}
int mx(int a,int b) {return a>b?a:b;}
void add(int u,int v,int cap)
{
    e[tot].v=v;         e[tot].nxt=head[u];
    /*e[tot].dis=dis;*/     e[tot].cap=cap;
    head[u]=tot++;

    e[tot].v=u;         e[tot].nxt=head[v];
    /*e[tot].dis=-dis;*/    e[tot].cap=0;
    head[v]=tot++;
}
// 仅有一次的BFS为ISAP节省了不少时间
bool bfs()
{
    //数组模拟queue
    int u,v,i;
    for(i=0;i<=t;++i) vis[i]=num[i]=0;
    // memset(vis, 0, sizeof(vis));
    // memset(num, 0, sizeof(num));
    fro = rea = 0;
    q[rea] = t; ++rea;
    vis[t] = 1;
    d[t] = 0;
    ++num[d[t]];

    while (rea>fro)
    {
        u = q[fro]; ++fro;
        for (i=head[u]; i!=-1; i=e[i].nxt)
        {
            v=e[i].v;
            if (!vis[v] && e[i^1].cap )
            {
                vis[v] = true;
                d[v] = d[u] + 1;
                ++num[d[v]];
                q[rea] = v; ++rea;
            }
        }
    }
    return vis[s];
}
// 增广
int augment()
{
    int  flow = inf, i;
    cg = t;
    // 从汇点到源点通过 p 追踪增广路径, flow 为一路上最小的残量
    while (cg != s) {
        i = bk[cg];
        if(flow>=e[i].cap)
        {
            flow = e[i].cap;
            ins = e[i^1].v;
            //用来加速寻找,在最小流量断开的地方重新开始寻找
            //嗯,等一下 我这个是从终点往起点寻找,而确定增光路径是从起点到终点
            //那么起点是河流的上游,那么回溯的河段应该尽可能的往上游靠近
            //所以应该将flow>e[i].cap的大于号改成大于等于号
        }
        cg = e[i^1].v;
    }
    cg = t;
    // 从汇点到源点更新流量
    while (cg != s) {
        i = bk[cg];
        e[i].cap -= flow;
        e[i^1].cap += flow;
        cg = e[i^1].v;
    }
    return flow;
}
//由于每次修改层次的时候,都是在到剩下子节点的距离中挑选最短的加1 所以层次分明不会出现死循环
int max_flow()
{
    int flow = 0,i,u,v;
    bool advanced;
    if(bfs()==false) return 0;
    //不一定是从s到t,你要知道统计每个层次的点的个数是全局统计的
    u = s;
    memcpy(cur, head, sizeof(head));
    while (d[s] < t+1)          //层次问题!!! 一定要保证t是最大的
    //终点是0,那么起点所在层次最多是N-1 同理,不是d[s]<t
    {
        if (u == t)
        {
            flow += augment();
            u = ins;    //pay speed up
        }
        advanced = false;
        for (i = cur[u]; i!=-1; i=e[i].nxt)
        {
            v = e[i].v;
            if (e[i].cap && d[u] == d[v] + 1)
            {
                advanced = true;
                bk[v] = i;
                cur[u] = i;
                u = v;
                break;
            }
        }
        if (!advanced)
        { // retreat
            int m = t+1;        //层次问题!!! 一定要保证t是最大的
            for (i = head[u]; i != -1; i=e[i].nxt)
            {
                if (e[i].cap&&m>d[e[i].v])
                {
                    cur[u] = i;
                    m = d[e[i].v];
                }
            }
            if (--num[d[u]] == 0) break; // gap 优化
            ++num[d[u] = m+1];
            //我以前一直在想 如果没有找到怎么办呢 现在发现原来找不到的话距离会被赋成N+1
            if (u != s)
                u = e[bk[u]^1].v;
        }
    }
    return flow;
}

void init()
{
    tot=0;
    memset(head,-1,sizeof(head));   //pay
}
char times[maxn][maxn],haves[maxn][maxn];
//最大流部分没有什么问题了 关键在于终点源点、编号分配、题目理解建图上面
int main()
{
    freopen("in.txt","r",stdin);
    sc("%d",&T);
    s=0, bk[0]=-1;
    int i,j,u,v,id,can,kase=0,who,LI;
    while(T--)
    {
        ++kase;
        sc("%d%d",&N,&D);
        init(); sp = LI = 0;
        for(i=0;i<N;++i) sc("%s",times[i]);
        for(i=0;i<N;++i) sc("%s",haves[i]);
        M=strlen(times[0]); delta  = (N)*(M); t=2*delta+1; s=2*delta;
        for(i=0;i<N;++i) for(j=0;j<M;++j)
        {
            can = times[i][j]-‘0‘;
            if(can==0) continue;
            id = i*(M) + (j);
            add(id,id + delta,can );
        }
        for(i=0;i<N;++i) for(j=0;j<M;++j)
        {
            if(haves[i][j]!=‘L‘) continue;
            id = i*(M) + (j);
            add(s,id,1);
            ++LI;
        }
        for(i=0;i<N;++i) for(j=0;j<M;++j)
        {
            if(times[i][j]==‘0‘) continue; //这里写成过有没有蜥蜴的那个矩阵
            id = i*(M) + (j);
            if(i-D<0||i+D>N-1||j-D<0||j+D>M-1)
            {

                add(id+delta,t,inf);
            }
            for(u=mx(0,i-D);u<=mi(N-1,i+D);++u) for(v=mx(0,j-D);v<=mi(M-1,j+D);++v)
            {
                //下面这一行居然是决胜关键 可是废点不是完全不会走吗
                if(times[u][v]==‘0‘) continue;
                if(u==i&&v==j) continue;
                //这是最终确定的可跳跃方式,他这个居然是曼哈顿距离 待会交一下看一下是不是这样 wa 了
                if(abs(u-i)+abs(v-j)>D) continue;
                //pt("i=%d,j=%d,u=%d,v=%d\n",i,j,u,v);
                who = u*(M) + (v);
                add(id+delta,who,inf);
            }
        }
        sp = max_flow();
        int ans = LI-sp;
        if(ans==0) printf("Case #%d: no lizard was left behind.\n",kase);
        else if(ans==1) printf("Case #%d: 1 lizard was left behind.\n",kase);
        else printf("Case #%d: %d lizards were left behind.\n",kase,ans);
    }
    return 0;
}

//http://www.renfei.org/blog/isap.html 带解释的//https://www.cnblogs.com/bosswnx/p/10353301.html 形式和我的比较相近的 #include<cstdio>#include<cstring>#include<cmath>using namespace std;#define maxe 100024  //pay  双向边 一共10万条路 双向就是20万 反边就是40万#define maxv 1024    //pay#define maxn 25    //pay#define sc scanf#define pt printf#define rep(i,a,b)  for(int i=(a);i<(b);++i)const int inf = 0x3f3f3f3f; int cg,sp,ins;  //cg change sp是总流量 ins是加速回溯点int N,M,T,D ,s,t,delta;int q[maxv],fro,rea;typedef struct ed{    int v,nxt,cap; //dis}ed;ed e[maxe];int tot,head[maxv],cur[maxv],vis[maxv],bk[maxv],d[maxv],num[maxv]; //int mi(int a,int b) {return a<b?a:b;}int mx(int a,int b) {return a>b?a:b;}void add(int u,int v,int cap){    e[tot].v=v;         e[tot].nxt=head[u];    /*e[tot].dis=dis;*/     e[tot].cap=cap;    head[u]=tot++;
    e[tot].v=u;         e[tot].nxt=head[v];    /*e[tot].dis=-dis;*/    e[tot].cap=0;    head[v]=tot++;} // 仅有一次的BFS为ISAP节省了不少时间 bool bfs(){    //数组模拟queue    int u,v,i;    for(i=0;i<=t;++i) vis[i]=num[i]=0;    // memset(vis, 0, sizeof(vis));    // memset(num, 0, sizeof(num));    fro = rea = 0;    q[rea] = t; ++rea;    vis[t] = 1;    d[t] = 0;    ++num[d[t]];        while (rea>fro)     {        u = q[fro]; ++fro;        for (i=head[u]; i!=-1; i=e[i].nxt)         {            v=e[i].v;            if (!vis[v] && e[i^1].cap )             {                vis[v] = true;                d[v] = d[u] + 1;                ++num[d[v]];                q[rea] = v; ++rea;            }        }    }    return vis[s];}// 增广int augment(){    int  flow = inf, i;    cg = t;    // 从汇点到源点通过 p 追踪增广路径, flow 为一路上最小的残量    while (cg != s) {        i = bk[cg];        if(flow>=e[i].cap)        {            flow = e[i].cap;            ins = e[i^1].v;                 //用来加速寻找,在最小流量断开的地方重新开始寻找            //嗯,等一下 我这个是从终点往起点寻找,而确定增光路径是从起点到终点            //那么起点是河流的上游,那么回溯的河段应该尽可能的往上游靠近            //所以应该将flow>e[i].cap的大于号改成大于等于号        }        cg = e[i^1].v;    }    cg = t;    // 从汇点到源点更新流量    while (cg != s) {        i = bk[cg];        e[i].cap -= flow;        e[i^1].cap += flow;        cg = e[i^1].v;    }    return flow;}//由于每次修改层次的时候,都是在到剩下子节点的距离中挑选最短的加1 所以层次分明不会出现死循环 int max_flow(){    int flow = 0,i,u,v;    bool advanced;    if(bfs()==false) return 0;    //不一定是从s到t,你要知道统计每个层次的点的个数是全局统计的    u = s;    memcpy(cur, head, sizeof(head));    while (d[s] < t+1)          //层次问题!!! 一定要保证t是最大的     //终点是0,那么起点所在层次最多是N-1 同理,不是d[s]<t    {        if (u == t)         {            flow += augment();            u = ins;    //pay speed up        }        advanced = false;        for (i = cur[u]; i!=-1; i=e[i].nxt)         {             v = e[i].v;            if (e[i].cap && d[u] == d[v] + 1)             {                advanced = true;                bk[v] = i;                cur[u] = i;                u = v;                break;            }        }        if (!advanced)         { // retreat            int m = t+1;        //层次问题!!! 一定要保证t是最大的             for (i = head[u]; i != -1; i=e[i].nxt)            {                if (e[i].cap&&m>d[e[i].v])                {                    cur[u] = i;                    m = d[e[i].v];                }            }            if (--num[d[u]] == 0) break; // gap 优化            ++num[d[u] = m+1];             //我以前一直在想 如果没有找到怎么办呢 现在发现原来找不到的话距离会被赋成N+1            if (u != s)                u = e[bk[u]^1].v;        }    }    return flow;}
void init(){    tot=0;    memset(head,-1,sizeof(head));   //pay }char times[maxn][maxn],haves[maxn][maxn];//最大流部分没有什么问题了 关键在于终点源点、编号分配、题目理解建图上面int main(){    freopen("in.txt","r",stdin);    sc("%d",&T);    s=0, bk[0]=-1;    int i,j,u,v,id,can,kase=0,who,LI;    while(T--)    {        ++kase;        sc("%d%d",&N,&D);        init(); sp = LI = 0;        for(i=0;i<N;++i) sc("%s",times[i]);        for(i=0;i<N;++i) sc("%s",haves[i]);        M=strlen(times[0]); delta  = (N)*(M); t=2*delta+1; s=2*delta;        for(i=0;i<N;++i) for(j=0;j<M;++j)        {            can = times[i][j]-‘0‘;            if(can==0) continue;             id = i*(M) + (j);            add(id,id + delta,can );        }        for(i=0;i<N;++i) for(j=0;j<M;++j)        {            if(haves[i][j]!=‘L‘) continue;            id = i*(M) + (j);            add(s,id,1);            ++LI;        }        for(i=0;i<N;++i) for(j=0;j<M;++j)        {            if(times[i][j]==‘0‘) continue; //这里写成过有没有蜥蜴的那个矩阵            id = i*(M) + (j);            if(i-D<0||i+D>N-1||j-D<0||j+D>M-1)            {                                add(id+delta,t,inf);            }             for(u=mx(0,i-D);u<=mi(N-1,i+D);++u) for(v=mx(0,j-D);v<=mi(M-1,j+D);++v)            {            //下面这一行居然是决胜关键 可是废点不是完全不会走吗                 if(times[u][v]==‘0‘) continue;                if(u==i&&v==j) continue;                //这是最终确定的可跳跃方式,他这个居然是曼哈顿距离 待会交一下看一下是不是这样 wa 了                if(abs(u-i)+abs(v-j)>D) continue;                //pt("i=%d,j=%d,u=%d,v=%d\n",i,j,u,v);                who = u*(M) + (v);                add(id+delta,who,inf);            }        }        sp = max_flow();        int ans = LI-sp;        if(ans==0) printf("Case #%d: no lizard was left behind.\n",kase);        else if(ans==1) printf("Case #%d: 1 lizard was left behind.\n",kase);        else printf("Case #%d: %d lizards were left behind.\n",kase,ans);    }    return 0;}

原文地址:https://www.cnblogs.com/lighten-up-belief/p/11367952.html

时间: 2024-10-20 21:36:46

网络流强化-HDU2732的相关文章

网络流强化-POJ2516

k种货物分开求解最小费用最大流,主要减少了寻找最短路的时间. #include<queue> #include<cstdio> #include<cstring> using namespace std; #define maxe 256000 //pay #define maxv 5120 //pay #define maxn 55 //pay #define sc scanf #define pt printf #define rep(i,a,b) for(int

网络流强化-HDU 3338-上下界限制最大流

题意是: 一种特殊的数独游戏,白色的方格给我们填1-9的数,有些带数字的黑色方格,右上角的数字代表从他开始往右一直到边界或者另外一个黑格子,中间经过的白格子的数字之和要等于这个数字:左下角的也是一样的意思,只是作用对象成了它下方的白格子. 思路: 既然所有行的数字之和等于所有列的数字之和,那么我们可以将行方向(向右)的点作为与源点连接的点,列方向(向下)的点作为与汇点连接的点. 由于向右和向下的点可能在同一块方格里面,以及我们需要设置每个白格子的容量,所以我们需要拆点. 题目要求填1-9的数,所

HDU-2732 (Leapin&#39; Lizards) 网络流

网络流真的博大精深 题意: 在一个迷宫中,新进了一批小蜥蜴. 然而因为一些原因,出口被封住了,而且迷宫燃起了大火,现在小蜥蜴们急着离开,想要求助于ACMer们,哈哈-- 把蜥蜴所在的坐标当做一根有高度为k的柱子,现在每有一只蜥蜴跳出格子,格子的柱子高度就会减少一,直到为0,表示这个格子不能在通过蜥蜴. 题目规定,小蜥蜴们可以从当前格子往周围跳出小于等于d距离的长度.当蜥蜴跳出迷宫的边界,就算这只蜥蜴存活下来了,而那些不能逃出去的,就只能等着被烤成烤肉了.现在想要让更多的蜥蜴逃出,求最后剩下的蜥蜴

hdu2732 Leapin&#39; Lizards (网络流dinic)

D - Leapin' Lizards Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description Your platoon of wandering lizards has entered a strange room in the labyrinth you are exploring. As you are looking around for hidden treasur

hdu2732 (Leapin&#39; Lizards)

题目链接:传送门 题目大意:给你 n,m  n:有几行图,m是一个人最多跳m个曼哈顿距离. 给你两张图,第一张图数字为0表示没有柱子,否则有柱子且只能跳出去 x 次(x为当前字符代表的数字) 第二张图 '.'表示没人,'L'表示当前位置有一个人,问通过柱子之间跳跃最后最少有多少人不能逃出去. 逃出去只能是柱子与边界的最小曼哈顿距离小于等于 m. 题目思路:拆点网络流 首先虚拟源汇点 S,T 其实这道题构图建模不难想,既然某个柱子只能跳 x 次,那就把它拆为两点,连边容量为 x,这样保证合法性.

hiho 第118周 网络流四&#183;最小路径覆盖

描述 国庆期间正是旅游和游玩的高峰期. 小Hi和小Ho的学习小组为了研究课题,决定趁此机会派出若干个调查团去沿途查看一下H市内各个景点的游客情况. H市一共有N个旅游景点(编号1..N),由M条单向游览路线连接.在一个景点游览完后,可以顺着游览线路前往下一个景点. 为了避免游客重复游览同一个景点,游览线路保证是没有环路的. 每一个调查团可以从任意一个景点出发,沿着计划好的游览线路依次调查,到达终点后再返回.每个景点只会有一个调查团经过,不会重复调查. 举个例子: 上图中一共派出了3个调查团: 1

JavaScript强化教程 - 六步实现贪食蛇

本文为H5EDU机构官方的HTML5培训教程,主要介绍贪食蛇JavaScript强化教程 1.首先创建div 并且给div加样式 <div id="pannel" style="width: 500px;height: 500px;z-index: 1;opacity: 0.5"></div> 给 <style...> 地图(div和表格).所有的块(蛇头,食物身体加样式) 2.创建地图     document.write(&q

POJ2584 T-Shirt Gumbo 二分图匹配(网络流)

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 const int inf=0x3f3f3f3f; 6 const int sink=30; 7 8 struct Edge 9 { 10 int to; 11 int next; 12 int capacity; 13 14 void assign(int t,int n,int c) 15 { 16 to=t; next=n; ca

UVA 1306 - The K-League(网络流)

UVA 1306 - The K-League 题目链接 题意:n个球队,已经有一些胜负场,现在还有一些场次,你去分配胜负,问每支球队有没有可能获胜 思路:网络流公平分配模型,把场次当作任务,分配给人,然后先贪心,枚举每个人,让这些人能赢的都赢,剩下的去建图,每个源点连向比赛容量为场次,每个比赛连向2个球队,容量无限大,每个球队连向汇点,容量为每个的人的总和减去当前已经赢的,建完图跑一下最大流,然后判断源点流出的是否都满流即可 代码: #include <cstdio> #include &l