• [BZOJ2324][ZJOI2011][最小费用最大流]营救皮卡丘


    [Problem Description]
    皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路。
    火箭队一共同拥有N个据点,据点之间存在M条双向道路。据点分别从1到N标号。小智一行K人从真新镇出发,营救被困在N号据点的皮卡丘。为了方便起见,我们将真新镇视为0号据点,一開始K个人都在0号点。
    因为火箭队的重重布防,要想摧毁K号据点,必须依照顺序先摧毁1到K-1号据点,而且,假设K-1号据点没有被摧毁,因为防御的连锁性,小智一行不论什么一个人进入据点K,都会被发现,并产生严重后果。因此,在K-1号据点被摧毁之前,不论什么人是不可以经过K号据点的。
    为了简化问题,我们忽略战斗环节,小智一行不论什么一个人经过K号据点即觉得K号据点被摧毁。被摧毁的据点依旧是能够被经过的。
    K个人是能够分头行动的,仅仅要有不论什么一个人在K-1号据点被摧毁之后,经过K号据点,K号据点就被摧毁了。显然的,仅仅要N号据点被摧毁,皮卡丘就得救了。
    野外的道路是不安全的,因此小智一行希望在摧毁N号据点救出皮卡丘的同一时候,使得K个人所经过的道路的长度总和最少。
    请你帮助小智设计一个最佳的营救方案吧!
    [Algorithm]
    最小费用最大流
    [Analysis]
    题目有几个关键点:
    1.每个点都必须有人经过
    2.经过j点时,0~j-1必须都经过了才干够
    由此能够构造出一个网络流的模型。
    因为每一个节点的第一次訪问,必然是由小于它的节点完毕的,所以先用floyd预处理dis[k][i][j]表示i到j仅仅经过小于等于k的点的最短路。建图例如以下
    1.S->0 cap=k cost=0
    2.i->T cap=1 cost=0
    3.S->i+n cap=1 cost=0
    4.i+n->j cap=INF cost=dis[j][i][j]
    1是用来限制人数的,2则保证每一个点经过一次,因为第一次经过该点时流向了T消耗了流量,所以每一个点给予补充流量,即建图3。4则是从一个刚刚第一次经过的点继续訪问其他点。这样建图能够发现,每一个点的第一次訪问都被转化成了一条从S出发的流且没有交叉,这样能够随意调整訪问顺序,保证了j訪问之前0~j-1都已经訪问过
    [Pay Attention]
    因为floyd要用邻接矩阵,注意推断重边的情况取最小。
    还有要从这样的点至少经过一次的建模中吸取经验
    [Code]
    /**************************************************************
        Problem: 2324
        User: gaotianyu1350
        Language: C++
        Result: Accepted
        Time:392 ms
        Memory:7620 kb
    ****************************************************************/
     
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <cstdlib>
    #include <queue>
    #include <iostream>
    using namespace std;
     
    #define MAXN 350
    #define MAXM 300000
    #define INF  0x3f3f3f3f
     
    int point[MAXN], next[MAXM], v[MAXM], flow[MAXM], cap[MAXM], w[MAXM];
    int lastedge[MAXN], dis[MAXN];
    bool check[MAXN];
    int tot = -1;
     
    int n, m, people;
    int map[MAXN][MAXN];
     
    inline void init()
    {
        memset(map, 0x7f, sizeof(map));
        memset(point, -1, sizeof(point));
        memset(next, -1, sizeof(next));
        tot = -1;
    }
     
    inline void addedge(int x, int y, int theCap, int theDis)
    {
        tot++;
        next[tot] = point[x]; point[x] = tot; 
        v[tot] = y; flow[tot] = 0; cap[tot] = theCap; w[tot] = theDis;
        tot++;
        next[tot] = point[y]; point[y] = tot;
        v[tot] = x; flow[tot] = 0; cap[tot] = 0; w[tot] = - theDis;
    }
     
    inline int addflow(int s, int t)
    {
        int now = t;
        int ans = INF;
        while (now != s)
        {
            int temp = lastedge[now];
            ans = min(ans, cap[temp] - flow[temp]);
            now = v[lastedge[now] ^ 1];
        }
        now = t;
        while (now != s)
        {
            flow[lastedge[now]] += ans;
            flow[lastedge[now] ^ 1] -= ans;
            now = v[lastedge[now] ^ 1];
        }
        return ans;
    }
     
    bool spfa(int s, int t, int &maxflow, int &mincost)
    {
        queue<int> q;
        while (!q.empty()) q.pop();
        memset(dis, 0x7f, sizeof(dis));
        memset(check, 0, sizeof(check));
        dis[s] = 0; check[s] = true; q.push(s);
        while (!q.empty())
        {
            int now = q.front(); q.pop();
            check[now] = false;
            for (int temp = point[now]; temp != -1; temp = next[temp])
                if (flow[temp] < cap[temp] && dis[now] + w[temp] < dis[v[temp]])
                {
                    dis[v[temp]] = dis[now] + w[temp];
                    lastedge[v[temp]] = temp;
                    if (!check[v[temp]])
                        check[v[temp]] = true, q.push(v[temp]);
                }
        }
        if (dis[t] > INF) return false;
        int add = addflow(s, t);
        maxflow += add;
        mincost += add * dis[t];
        return true;
    }
     
    inline int solve(int s, int t)
    {
        int maxflow = 0, mincost = 0;
        while (spfa(s, t, maxflow, mincost));
        /*{
            int tiaoshi = 1;
            tiaoshi++;
        }*/
        //printf("mflow :%d
    ", maxflow);
        return mincost;
    }
     
    inline void build(int start, int end)
    {
        addedge(start, 0, people, 0);
        for (int i = 1; i <= n; i++)
        {
            addedge(i, end, 1, 0);
            addedge(start, i + n, 1, 0);
            //addedge(i, i + n, INF, 0);
        }
        for (int k = 0; k <= n; k++)
        for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            if (i != j)
            {
                if (map[i][k] < INF && map[k][j] < INF)
                    map[i][j] = min(map[i][j], map[i][k] + map[k][j]);
                if (k == j && i < j && map[i][j] < INF)
                    addedge(i == 0 ? 0 : i + n, j, INF, map[i][j]);       
            }   
    }
     
    int main()
    {
        //freopen("input.txt", "r", stdin);
        init();
        scanf("%d%d%d", &n, &m, &people);
        for (int i = 1; i <= m; i++)
        {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            if (z < map[x][y])
                map[x][y] = map[y][x] = z;
        }
        build(2 * n + 1, 2 * n + 2);
        printf("%d
    ", solve(2 * n + 1, 2 * n + 2));
    }


  • 相关阅读:
    hdu 4002 Find the maximum
    hdu 2837 坑题。
    hdu 3123
    zoj Treasure Hunt IV
    hdu 2053 Switch Game 水题一枚,鉴定完毕
    poj 1430 Binary Stirling Numbers
    hdu 3037 Saving Beans
    hdu 3944 dp?
    南阳oj 求N!的二进制表示最低位的1的位置(从右向左数)。
    fzu 2171 防守阵地 II
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/3845444.html
Copyright © 2020-2023  润新知