【BZOJ-1449&2895】球队收益&球队预算 最小费用最大流

1449: [JSOI2009]球队收益

Time Limit: 5 Sec  Memory Limit: 64 MB
Submit: 648  Solved: 364
[Submit][Status][Discuss]

Description

Input

Output

一个整数表示联盟里所有球队收益之和的最小值。

Sample Input

3 3
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1

Sample Output

43

HINT

Source

2895: 球队预算

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 51  Solved: 50
[Submit][Status][Discuss]

Description

在一个篮球联赛里,有n支球队,球队的支出是和他们的胜负场次有关系的,具体来说,第i支球队的赛季总支出是Ci*x^2+Di*y^2,Di<=Ci。(赢得多,给球员的奖金就多嘛)

其中x,y分别表示这只球队本赛季的胜负场次。现在赛季进行到了一半,每只球队分别取得了a[i]场胜利和b[i]场失利。而接下来还有m场比赛要进行。问联盟球队的最小总支出是多少。

Input

第一行n,m

接下来n行每行4个整数a[i],b[i],Ci,Di

再接下来m行每行两个整数s,t表示第s支队伍和第t支队伍之间将有一场比赛,注意两只队间可能有多场比赛。

Output

输出总支出的最小值。

Sample Input

3 3
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1

Sample Output

43
Data Limit
对于20%的数据2<=n<=10,0<=m<=20
对于100%的数据2<=n<=5000,0<=m<=1000,0<=di<=ci<=10,0<=a[i],b[i]<=50.

HINT

Source

Solution

idea很好的建图,不能考虑直接建,而应该利用增量的思想

个人的想法是这样的:

源到各比赛连边,容量为1,费用为0

建每个球队分成两个,由个比赛连向两只球队a,b,再由a连b‘,b连a‘,分别表示a赢了这场比赛或b赢了这场比赛

然后由各个点i‘连向汇容量为inf,费用为0

然后预处理出来已比完的比赛胜负的价值,加上最小费用即可;

思路是考虑一场比赛的胜负对于结果的贡献,从这场比赛结束后对结果的增量来考虑,但显然这种方法并不是很优,但理论上可A

那么同样的思想,换一种方法建图

不妨先假设后面的M场比赛中双方都是输家,这样我们只要在模型中表示一方成为赢家即可。

此应该有一个初步的模型了:对于N支球队和M场比赛各建一个点,从源向每场比赛连流量1费用0的边,从比赛向参与这场比赛的两支队伍各连一条流量1费用0的边。剩下的就是队伍收益的费用表示了。

多赢一场比赛产生的收益。即(C*(w+1)^2+D*(l-1)^2)-(C*w^2+D*l^2)=2w*C-2l*D+C+D。对于第i支队伍,假设后M场中i参加的有x场,那么最初w=win,l=lose+x,之后每赢一场w++,l--。我们从第i支队伍的点向汇连x条边,分别代表第i支队伍赢了j场比赛时相对赢j-1场时收益的增量。由于增量一定越来越大(平方嘛),所以流量最先流过的一定是费用较小的边,即j最小的边。

                                  -----by   huzecong

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘)f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define maxn 5100+1100
#define maxm 100100
int n,m,ans,S,T,num[5010];
struct Teamnode{int win,lose,C,D;}team[5010];
struct Edgenode{int to,next,cap,cost,from;}edge[maxm];
int head[maxn],cnt=1;
void add(int u,int v,int w,int c)
{cnt++;edge[cnt].from=u;edge[cnt].to=v;
edge[cnt].next=head[u];head[u]=cnt;
edge[cnt].cost=c;edge[cnt].cap=w;}
void insert(int u,int v,int w,int c){add(u,v,w,c);add(v,u,0,-c);}
#define inf 0x7fffffff
int q[maxn],h,t; int dis[maxn];bool mark[maxn];
bool spfa()
{
    memset(mark,0,sizeof(mark));
    for (int i=S; i<=T; i++) dis[i]=inf;
    h=0,t=1;
    q[0]=T; mark[T]=1; dis[T]=0;
    while (h<t)
        {
            int now=q[h++]; mark[now]=0;
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i^1].cap && dis[now]+edge[i^1].cost<dis[edge[i].to])
                    {
                        dis[edge[i].to]=dis[now]+edge[i^1].cost;
                        if (!mark[edge[i].to])
                            mark[edge[i].to]=1,q[t++]=edge[i].to;
                    }
        }
    return dis[S]!=inf;
}
int dfs(int loc,int low)
{
    mark[loc]=1;
    if (loc==T) return low;
    int w,used=0;
    for (int i=head[loc]; i; i=edge[i].next)
        if (!mark[edge[i].to] && edge[i].cap && dis[edge[i].to]==dis[loc]-edge[i].cost)
            {
                w=dfs(edge[i].to,min(low-used,edge[i].cap));
                edge[i].cap-=w; edge[i^1].cap+=w;
                ans+=w*edge[i].cost; used+=w;
                if (used==low) return low;
            }
    return used;
}
int zkw()
{
    int tmp=0;
    while (spfa())
        {
            mark[T]=1;
            while (mark[T])
                {
                    memset(mark,0,sizeof(mark));
                    tmp+=dfs(S,inf);
                }
        }
    return tmp;
}
int power(int x){return x*x;}
void build()
{
    S=0,T=n+m+1;
    for(int a,b,i=1; i<=m; i++)
        insert(S,i,1,0),
        a=read(),b=read(),
        insert(i,a+m,1,0),insert(i,b+m,1,0),
        num[a]++,num[b]++;
    for(int i=1; i<=n; i++)
        team[i].lose+=num[i];
    for(int i=1; i<=n; i++)
        ans+=power(team[i].win)*team[i].C+power(team[i].lose)*team[i].D;
    for(int i=1; i<=n; i++)
         for(int j=1;j<=num[i];j++)
            insert(i+m,T,1,2*team[i].C*team[i].win+team[i].C+team[i].D-2*team[i].D*team[i].lose),
            team[i].lose--,team[i].win++;
}
int main()
{
    n=read(),m=read();
    for (int i=1; i<=n; i++)
        team[i].win=read(),team[i].lose=read(),
        team[i].C=read(),team[i].D=read();
    build();
    int Maxflow=zkw();
    printf("%d\n",ans);
    return 0;
}

吐槽一句..增广的过程中,一开始使用STL中的queue然后莫名的WA了?手写了个q...怒A...跑得飞快

时间: 2024-10-17 11:49:52

【BZOJ-1449&2895】球队收益&球队预算 最小费用最大流的相关文章

BZOJ 1877:[SDOI2009]晨跑(最小费用最大流)

晨跑DescriptionElaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑.仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑. 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街道,Elaxia只能从 一个十字路口跑向另外一个十字路口,街道之间只在十字路口处相交.Elaxia每天从寝室出发 跑到学校,保证寝室编号为1,学校编号为N. Elaxia的晨跑计划是按周期(包含若干天)进行的,由于他不喜欢走重复的路线,所以 在一个周期内,每天的晨跑路线都不会相交(在

【BZOJ1449/2895】[JSOI2009]球队收益/球队预算 最小费用最大流

[BZOJ2895]球队预算 Description 在一个篮球联赛里,有n支球队,球队的支出是和他们的胜负场次有关系的,具体来说,第i支球队的赛季总支出是Ci*x^2+Di*y^2,Di<=Ci.(赢得多,给球员的奖金就多嘛) 其中x,y分别表示这只球队本赛季的胜负场次.现在赛季进行到了一半,每只球队分别取得了a[i]场胜利和b[i]场失利.而接下来还有m场比赛要进行.问联盟球队的最小总支出是多少. Input 第一行n,m 接下来n行每行4个整数a[i],b[i],Ci,Di 再接下来m行每

BZOJ 1449 球队收益(最小费用最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1449 题意: 思路:首先,我们假设后面的M场比赛两方都是输的,即初始时的lose[i]再加上i参加的场次.这样,后面对于i,每赢一场的收益增加值为: 之后win[i]++,lose[i]--.至此,我们得到建图的方法: (1)源点到每场比赛连流量1,费用0: (2)每场比赛向双方连流量1,费用0: (3)每个人到汇点连x条边(x为该人在M场比赛中出现的次数),流量1,费用为上面计算出的

【bzoj1449/bzoj2895】[JSOI2009]球队收益/球队预算 费用流

题目描述 输入 输出 一个整数表示联盟里所有球队收益之和的最小值. 样例输入 3 3 1 0 2 1 1 1 10 1 0 1 3 3 1 2 2 3 3 1 样例输出 43 题解 费用流 由于存在一个赢一个输,比较难算.我们可以先假设它们都输掉,然后再安排赢的情况. 设fi为i还要打的比赛数目,那么初始的收益为∑ci*wi^2+di*(li+fi)^2. S->每场比赛,容量为1,费用为0. 每场比赛->比赛的两队,容量为1,费用为0. 因为费用的改变是包含平方的,所以我们需要拆边来做. 第

BZOJ 2324 营救皮卡丘(最小费用最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2324 题意:n+1个城市(0到n).初始时K个 人都在0城市.城市之间有距离.要求(1)遍历完n个城市(有一个人遍历了某个城市就算这个城市被遍历了):(2)遍历i城市前必须遍历完前i-1个城 市,并且在遍历前i-1个城市时不能经过大于等于i的城市.在满足(1)(2)的前提下使得K个人走的总距离最小. 思路:我们先看在实际情况下可以怎么走. (1)某个人遍历完某个城市后停在那里,以后不再

BZOJ 1927 星际竞速(最小费用最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1927 题意:一个图,n个点.对于给出的每条边 u,v,w,表示u和v中编号小的那个到编号大的那个的时间为w.另外有n个值Ai,表示从任何一个点到达i点的时间为Ai.初始时你在n个点之外的一个 点上,我们称其为初始点B.要求从B出发,遍历n个点每个点一次,求最小时间.显然开始你只能使用Ai从B到达n个点中的某个点,因为B到n个点中没有其 他的边. 思路:因为最后停在了某个点上,那么从B出

BZOJ 1061 志愿者招募(最小费用最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1061 题意:申奥成功后,布布经过不懈努力,终于 成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人. 布布通过了解得知,一共有M 类志愿者可以招募.其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元.新官上任三把火,为了出色地完成自己的工作,布

BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流

传送门 BZOJ 2668 题解 同时分别限制流入和流出次数,所以把一个点拆成三个:入点in(x).中间点mi(x).出点ou(x). 如果一个格子x在初始状态是黑点,则连(S, mi(x), 1, 0) 如果x在目标状态是黑点,则连(mi(x), T, 1, 0) 设x的交换次数限制是w 如果x在两种状态中颜色相同,则连(in(x), mi(x), w / 2, 0), (mi(x), ou(x), w / 2, 0) 如果x只在初始状态为黑色,则连(in(x), mi(x), w / 2,

bzoj 1927 [Sdoi2010]星际竞速(最小费用最大流)

1927: [Sdoi2010]星际竞速 Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 1576  Solved: 954[Submit][Status][Discuss] Description 10 年一度的银河系赛车大赛又要开始了.作为全银河最盛大的活动之一, 夺得这个项目的冠军无疑是很多人的梦想,来自杰森座 α星的悠悠也是其中之一. 赛车大赛的赛场由 N 颗行星和M条双向星际航路构成,其中每颗行星都有 一个不同的引力值.大赛要求车手们从一