HDU 5644 King's Pilots 费用流

本文解析了一个基于费用流算法的比赛题目“King's Pilots”,介绍了如何通过构建双层图来解决飞行员调度问题,实现成本最小化。

King's Pilots

题目连接:

http://acm.hdu.edu.cn/showproblem.php?pid=5644

Description

The military parade will last for n days. There is a aerobatic flight shows everyday during the parade. On the ith day , Pi pilots are required. But the pilots are not willing to work continually without an extra salary for even two days , because they are extremely tired.

There are m Holiday formulations in this country. For each formulation j , that is: when a pilot works on a day , if you pay him Sj dollars he is willing to come back Tj days after that day.

For example , If a pilot work on the rth day , and Tj==1 then he will return to work on r+1th day

At the very beginning there are k pilots , but of course you can hire more pilots. However , training new pilots require P days and for each new pilot you need pay him Q dollars. (Which means you can only use new pilots on Pth day or later)

Now our great king needs you to plan all these things. There must be enough pilots everyday and the cost must be minimized. Please tell the king what is the lowest cost;

The N - 1 round, the next person of the person who is out in the last round counts off, and the person who report number n−1 is out.

And the last man is survivor. Do you know the label of the survivor?

Input

The first line contains a number T(T≤5), the number of testcases.

For each testcase, the first line contains a number n(n≤200)

On the second line , there are n numbers Pi(1≤i≤n) indicating the number of pilots needed on that day

On the third line , 3 numbers , m(1≤m≤5),P,Q

On the following m lines , each line has two numbers: Si , Ti

all input data x satisfy x∈[0,200]

Output

For each testcase, print a single number. The minimum cost.

If there is no solution , just put No solution

Sample Input

1
5 10
1 3 5 10 6
1 3 5
2 2

Sample Output

48

Hint

题意

有n天,每天需要p[i]个人,你一开始雇佣了k个人

你从第p天开始可以再花Q元雇佣一个人。

然后这些人只会工作一天。

但是有m种政策,可以使得这些人在休息t[i]天后只用花s[i]元就可以再让这些工人工作了。

题解:

费用流。

建图的话建两层。

第一层假装没有那m种政策,然后无脑建图就好了。

S向1连容量为k,花费为0的边。

每个点向T连容量为P[i],花费为0的边。

每个点i向i+1连容量为inf,花费为0的边。

然后P天后的,连容量为inf,花费为q的边。

第二层连政策。

S向每个点连容量为P[i],花费为0的边。

每个点向T[i]天后的第一层的点连容量为inf,花费为S[i]

每个点i向i+1连容量为inf,花费为0的边。

一层维护工作,一层维护政策。

然后check是否满流就好了。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10000;
const int MAXM = 100000;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to, next, cap, flow, cost;
    int x, y;
} edge[MAXM],HH[MAXN],MM[MAXN];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N, M;
char map[MAXN][MAXN];
void init()
{
    N = MAXN;
    tol = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int cap, int cost)//左端点,右端点,容量,花费
{
    edge[tol]. to = v;
    edge[tol]. cap = cap;
    edge[tol]. cost = cost;
    edge[tol]. flow = 0;
    edge[tol]. next = head[u];
    head[u] = tol++;
    edge[tol]. to = u;
    edge[tol]. cap = 0;
    edge[tol]. cost = -cost;
    edge[tol]. flow = 0;
    edge[tol]. next = head[v];
    head[v] = tol++;
}
bool spfa(int s, int t)
{
    queue<int>q;
    for(int i = 0; i < N; i++)
    {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i]. next)
        {
            int v = edge[i]. to;
            if(edge[i]. cap > edge[i]. flow &&
                    dis[v] > dis[u] + edge[i]. cost )
            {
                dis[v] = dis[u] + edge[i]. cost;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1) return false;
    else return true;
}
//返回的是最大流, cost存的是最小费用
int minCostMaxflow(int s, int t, int &cost)
{
    int flow = 0;
    cost = 0;
    while(spfa(s,t))
    {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1]. to])
        {
            if(Min > edge[i]. cap - edge[i]. flow)
                Min = edge[i]. cap - edge[i]. flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1]. to])
        {
            edge[i]. flow += Min;
            edge[i^1]. flow -= Min;
            cost += edge[i]. cost * Min;
        }
        flow += Min;
    }
    return flow;
}
const int inf = 1e9;
int P[205],p,q,S[205],T[205];
void solve()
{
    init();
    int m,n,k,sum=0;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&P[i]),sum+=P[i];
    scanf("%d%d%d",&m,&p,&q);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&S[i],&T[i]);
    int st=5000,ed=5001;
    for(int i=1;i<=n;i++)
    {
        addedge(st,i,P[i],0);
        addedge(n+i,ed,P[i],0);
    }
    addedge(st,n+1,k,0);
    for(int i=p;i<=n;i++)
        addedge(st,n+i,inf,q);
    for(int i=1;i<n;i++)
        addedge(i,i+1,inf,0);
    for(int i=1;i<n;i++)
        addedge(n+i,n+i+1,inf,0);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            if(i+T[j]<=n)
                addedge(i,n+i+T[j],inf,S[j]);
    }
    int ans1=0,ans2=0;
    ans1=minCostMaxflow(st,ed,ans2);
    if(ans1==sum)printf("%d\n",ans2);
    else printf("No solution\n");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值