Pollutant Control[USACO]

      这道题,我觉得难想到的是 “If there are multiple sets of truck routes that achieve the goal at minimum cost, choose one that shuts down the minimum number of routes.”,也就是如何在多个最小割中,取边数最少的方案。怎么处理这个条件,我想到的方法有点像像用背包问题:先求出最小割P。然后判断路线1到路线M,该路线是否可以在最小割中(判断方式是:去掉该边后的最小这P',如果P-P‘==lenth[i],则该边可以在最小割中),所有满足条件的边组成集合S,下面就是背包问题了,容量为P,从S中取边,用dfs取得所有解,选边数最小,字典序最小的即可。

 

      后来看过别人关于第二问的讨论之后,又一次意识到,我还是太弱了。通过设置权重,len[i]*1001 + 1 设为新的边长,因为边数最多为1000,所以这个+1并不会影响最小割的选择,同时这个+1又可以保证边数最少。于是,我们求出的新的最小割P,用P/1001 即为原始的最小割,P%1001则为此次最小割的边数。第三问,因为已经确定了边数最小,我们只要保证字典序最小即可:判断边i是否可以在最小割中,如可以,将此边从图中删除更新最小割,直至最小割变为0为止。

 

另外还需要注意的就是,两个仓库之间可以有多条路线~ 

时间复杂度为O(n^4), N最大为30,所以肯定不会超时的。

附代码:

/*
ID: zhangyc1
LANG: C++
TASK: milk6
*/
#include <fstream>
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
using namespace std;

struct Line 
{
    int nSt, nEd, nCost;
};
ofstream fileout("milk6.out");
int arrVisitList[31], arrVisitList2[31];// 访问队列
int arrNext[33], arrNext2[33];// 每个节点的已访问位置
long long arrCap[33][33], arrFlow[33][33], arrExtra[33];// 容量,流量,余流
long long arrCap2[33][33], arrFlow2[33][33], arrExtra2[33];
int arrHeight[33], arrHeight2[33];// 高度
const int C_W = 1001;
int N, M;
const int MAX_HEIGHT = 1000000;
Line arrLine[1000];
long long RelabelToFront();

void prepairData()
{
    memset(arrCap, 0, sizeof(arrCap));
    ifstream filein("milk6.in");
    filein >> N >> M;
    for (int i = 0; i < M; i++)
    {
        filein >> arrLine[i].nSt >> arrLine[i].nEd >> arrLine[i].nCost;
        // 这个..两个仓库间可能有多个线路
        arrCap[arrLine[i].nSt][arrLine[i].nEd] += arrLine[i].nCost * C_W + 1;
    }
    filein.close();
    for (int i = 1; i <= N; i++)
    {
        arrNext[i] = 1;
    }
    for (int i = 0; i < N - 1; i++)
    {
        arrVisitList[i] = i + 1;
    }
    memset(arrFlow, 0, sizeof(arrFlow));
    memset(arrExtra, 0, sizeof(arrExtra));
    memset(arrHeight, 0, sizeof(arrHeight));

    memcpy(arrVisitList2, arrVisitList, sizeof(arrVisitList));
    memcpy(arrNext2, arrNext, sizeof(arrNext));
    memcpy(arrCap2, arrCap, sizeof(arrCap));
    memcpy(arrFlow2, arrFlow, sizeof(arrFlow));
    memcpy(arrExtra2, arrExtra, sizeof(arrExtra));
    memcpy(arrHeight2, arrHeight, sizeof(arrHeight));
}

void RestoreData()
{
    memcpy(arrVisitList, arrVisitList2, sizeof(arrVisitList));
    memcpy(arrNext, arrNext2, sizeof(arrNext));
    memcpy(arrCap, arrCap2, sizeof(arrCap));
    memcpy(arrFlow, arrFlow2, sizeof(arrFlow));
    memcpy(arrExtra, arrExtra2, sizeof(arrExtra));
    memcpy(arrHeight, arrHeight2, sizeof(arrHeight));
}

void process()
{
    long long rs = RelabelToFront();
    long long nMinCost = rs / C_W;
    long long nNumLine = rs % C_W;
    fileout << nMinCost << " " << nNumLine << endl;
    if (nNumLine == 0)
        return;

    for (int i = 0; i < M; i++)
    {
        // 删除此条线路,判断其是否在最大流中
        arrCap[arrLine[i].nSt][arrLine[i].nEd] -= arrLine[i].nCost* C_W + 1;
        arrCap2[arrLine[i].nSt][arrLine[i].nEd] = arrCap[arrLine[i].nSt][arrLine[i].nEd];
        long long newRs = RelabelToFront();
        if (rs - newRs == arrLine[i].nCost* C_W + 1)
        {
            fileout << i + 1 << endl;
            rs = newRs;
            if (newRs == 0)
                break;
        }
        else
        {// 若不在,恢复数据
            arrCap[arrLine[i].nSt][arrLine[i].nEd] += arrLine[i].nCost* C_W + 1;
            arrCap2[arrLine[i].nSt][arrLine[i].nEd] = arrCap[arrLine[i].nSt][arrLine[i].nEd];
        }
    }
}

int main(){
    prepairData();
    process();
    fileout.close();
    return 0;
}
// 压入
void PushOper(int u, int v)
{
    long long nMin = min(arrExtra[u], arrCap[u][v] - arrFlow[u][v]);
    arrExtra[u] -= nMin, arrExtra[v] += nMin;
    arrFlow[u][v] += nMin, arrFlow[v][u] -= nMin;
}
// 重标注
void RelabelOper(int u)
{
    int nMin = MAX_HEIGHT;
    for (int i = 1; i <= N; i++)
    {
        if (arrCap[u][i] > arrFlow[u][i] && nMin > arrHeight[i])
            nMin = arrHeight[i];
    }
    arrHeight[u] = nMin + 1;
}
// 排空
void Discharge(int u)
{
    //cout << "Discharge u" << u << endl;
    while (arrExtra[u] > 0)
    {
        if (arrNext[u] <= N)
        {
            int v = arrNext[u];
            if (arrHeight[u] > arrHeight[v] && arrCap[u][v] > arrFlow[u][v])
                PushOper(u, v);
            else
                arrNext[u]++;
        }
        else
        {
            RelabelOper(u);
            arrNext[u] = 1;
        }
    }
}
// 前移
void MoveToFront(int nIndex)
{
    int nKey = arrVisitList[nIndex];
    for (int i = nIndex; i; i--)
    {
        arrVisitList[i] = arrVisitList[i - 1];
    }
    arrVisitList[0] = nKey;
}
// 重标注前移算法。结束前将数据恢复
long long RelabelToFront()
{
    arrHeight[1] = N;
    for (int i = 2; i <= N; i++)
    {
        if (arrCap[1][i] > 0)
        {
            arrExtra[1] -= arrCap[1][i];
            arrExtra[i] = arrCap[1][i];
            arrFlow[1][i] = arrCap[1][i];
            arrFlow[i][1] = -arrFlow[1][i];
        }
    }
    int nIndex = 1;
    while (nIndex < N - 1)
    {
        int u = arrVisitList[nIndex];
        int nOldHeight = arrHeight[u];
        Discharge(u);
        int nNewHeight = arrHeight[u];
        if (nOldHeight != nNewHeight)
        {
            MoveToFront(nIndex);
            nIndex = 1;
        }
        else
            nIndex++;
    }
    long long rs = arrExtra[N];
    RestoreData();
    return rs;
}

 

转载于:https://www.cnblogs.com/doublemystery/archive/2013/04/15/3022111.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值