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;
}