【例题】【网络流(费用流)】NKOJ 3738 学号

14 篇文章 0 订阅
2 篇文章 0 订阅

NKOJ 3738 学号
时间限制 : - MS 空间限制 : 65536 KB
评测说明 : 时限1000ms

问题描述
南开中学信竞班有N名同学,为方便管理,何老板给每个同学设置了一个学号(1到N之间的一个整数)。
今天,何老板发现,因为他的疏忽,某些同学的学号是相同的。他想将部分同学的学号做一下修改,使得N个同学的学号恰好构成一个1到N的排列。
但很多同学并不买账,他们不想轻易修改自己的学号。经过讨价还价,每个同学都希望修改后的学号与原学号近可能相近,每个同学都给出了一个他能接受的学号范围[A,B],如果超出他能接受的范围,他就会拒绝修改。每个同学还向何老板提出了一个修改代价V,比如何老板将第i个同学的学号从Xi改成了Xi’(Ai<=Xi’<=Bi),那么他需要提供给这位同学Vi*|Xi’-Xi|小时的合法游戏时间。在这段时间内该同学打游戏,何老板不得干涉他。
何老板希望能使得所有同学的学号恰好是1到N,但又希望同学们合法玩游戏的总时间尽可能少,问是否能满足何老板的要求。能输出最少游戏总时间,否则输出NO

输入格式
第一行,一个整数N,表示学生人数。
接下来N行,每行四个整数,描述一个学生。第i行为Xi,Ai,Bi,Vi,分别表示第i个同学现在的学号,他期望的新学号所在区间[Ai,Bi],和修改代价

输出格式
一行,一个整数表示答案。如果无解,输出NO

样例输入
5
1 1 2 3
1 1 5 1
3 2 5 5
4 1 5 10
3 3 3 1

样例输出
9

提示
1<=N<=200
1<=Ai<=Xi<=Bi<=N
1<=Vi<=1000

来源 改编自POI 2006 Szk-Schools

思路:原点流向各同学,费用0,流量1。各同学流向可以分配的学号,费用为对应代价,流量随意。学号流向汇点,费用0,流量1;

代码:

#include<cstdio>
#include<iostream>
#include<queue>
#include<vector>
#include<cmath>
#include<cstdlib>
using namespace std;
#define pb push_back 
const int inf=1e9;
const int need=500;

vector<int>fr,en,la,ww,cc;
int tot=-1,fi[need];
int dis[need],path[need];
bool mark[need];
int n,s,e,maxflow,mincost;

void add(int a,int b,int w,int c)
{
    tot++;fr.pb(a),en.pb(b),ww.pb(w),cc.pb(c),la.pb(fi[a]);fi[a]=tot;
    tot++;fr.pb(b),en.pb(a),ww.pb(-w),cc.pb(0),la.pb(fi[b]);fi[b]=tot;
}

bool findpath()
{
    for(int i=1;i<=e;i++) dis[i]=inf,path[i]=0,mark[i]=false;
    queue<int> q;
    dis[s]=0;mark[s]=true;q.push(s);
    int x,t,temp;
    #define y en[t]
    while(q.size())
    {
        x=q.front(),q.pop(),mark[x]=false;
        for(t=fi[x];t;t=la[t])
        {
            if(cc[t]<=0) continue;
            temp=dis[x]+ww[t];
            if(dis[y]>temp)
            {
                dis[y]=temp;
                path[y]=t;
                if(!mark[y])
                {
                    mark[y]=true;
                    q.push(y);
                }
            }
        }
    }
    #undef y
    return dis[e]<inf;
}

#define t path[x]
void addflow()
{
    int flow=inf;
    for(int x=e;x!=s;x=fr[t])
     flow=min(flow,cc[t]);
    maxflow+=flow;
    mincost+=flow*dis[e];
    for(int x=e;x!=s;x=fr[t])
    {
        cc[t]-=flow;
        cc[t^1]+=flow;
    }
}
#undef t

int main()
{
    add(0,0,0,0),add(0,0,0,0);
    scanf("%d",&n);
    s=n+n+1,e=s+1;
    for(int i=1;i<=n;i++) add(s,i,0,1);
    for(int i=n+1,n2=n<<1,j,x,a,b,v;i<=n2;i++)
    {
        scanf("%d%d%d%d",&x,&a,&b,&v);
        for(j=a;j<=b;j++) add(j,i,v*(abs(j-x)),1);
    }
    for(int i=n+1,n2=n<<1;i<=n2;i++) add(i,e,0,1);
    while(findpath()) addflow();
    if(maxflow!=n) {puts("NO");return 0;}
    else printf("%d",mincost);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值