首页 > 代码库 > [BZOJ2324][ZJOI2011][最小费用最大流]营救皮卡丘

[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\n", 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\n", solve(2 * n + 1, 2 * n + 2));
}