说在前面
并没有什么想说的,但是要保持格式=w=
题目
题面
给出N件单位时间任务,对于第i件任务,如果要完成该任务,需要占用[Si, Ti]间的某个时刻,且完成后会有Vi的收益。求最大收益。 一个时刻只能做一件任务,做一个任务也只需要一个时刻。
数据规模:N≤5000
BZOJ4276:1 ≤ Si ≤ Ti ≤ 5000,40s时限
BZOJ2034:1 ≤ Si ≤ Ti ≤
108
10
8
,1 ≤ Vi ≤
108
10
8
,10s时限
输入输出格式
输入格式:
第一行包含一个整数N,含义如题
接下来N行,每行3个整数(s,t,v),含义如题
输出格式:
输出最大的收益
解法
首先看到这道题,应该能反应过来是最大匹配问题。就是让每个任务找一个时间点与之匹配,并获得相应的收益
那么最简单的方式就是:每个任务向区间内所有的时间点连边,然后跑最大权匹配或者费用流。但是复杂度太高了,以至于上面两种数据规模都过不去…
线段树优化建边写法
我们发现,这个连边是有特点的,每个任务向时间点连边都是连续的。于是考虑使用线段树优化建边
首先源点向每个任务连边,容量1,费用为收益。然后每个任务向线段树内的区间连边,容量inf,费用0。最后线段树的每个叶子结点向汇点连边,容量1,费用0。然后跑费用流即可。
这样子就可以通过BZOJ4276
贪心优化最大权匹配
当时间的范围扩大到1e8的时候,即使使用线段树优化建边,线段树的节点也开不下了,于是继续优化算法
1.注意到,假设最后的匹配中不包含u任务,而包含了v任务(u和v的时间区间有交集,并且u的利润大于v),那么一定可以将v换成u,答案会变得更优。于是,我们可以贪心的进行匹配
2.匹配的过程中,肯定是尽量把任务匹配的时间尽可能放到前面去。换句话说,先把所有的任务按照起始时间升序排序,如果u可以匹配上1和2,那么u匹配1不会比匹配2更劣,因为2可能会和后面的点匹配,要把这个机会留给后面的任务。于是,可以把有效的时间点缩到N个。即,对于每个任务u(排序后),找到在u的起始时刻之后第一个没有被占用的时间点,并将之占用。任何一种最优匹配,都可以在把其中的空隙紧缩之后,让所有的匹配点落在这N个占用点上。
由贪心策略,我们把所有的任务按照利益降序排序,并把这些任务在 占用点 上匹配,如果一个任务匹配不上,那么就一定不在最优决策里。而且在匹配u的时候,如果当前时间点now没有被匹配,那么就把u和now匹配上,不然,就让那个结束位置更大的去匹配(即u和match[now]中取结束位置较大的那个),因为如果较大的那个都匹配不上,较小的那个也一定匹配不上,于是这样就省去了遍历所有边的过程,一次匹配复杂度降为 Θ(N) Θ ( N ) 。总复杂度为 Θ(N2) Θ ( N 2 )
下面是自带大常数的代码
BZOJ4276
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , tp = 1 , head[5000*15] , mint = 0x3f3f3f3f , maxt , ans ;
int S , T , id_c , rob[5005] ;
struct Path{
int pre , to , flow , fee ;
}p[5000*30] ;
void In( int t1 , int t2 , int t3 , int t4 ){
p[++tp] = ( Path ){ head[t1] , t2 , t3 , t4 } ; head[t1] = tp ;
p[++tp] = ( Path ){ head[t2] , t1 , 0 , -t4 } ; head[t2] = tp ;
}
struct Data{
int a , b , c ;
}d[5005] ;
struct Node{
int id ;
Node *ch[2] ;
} *root ;
void newNode( Node *&nd ){
nd = new Node() ;
nd->id = ++id_c ;
nd->ch[0] = nd->ch[1] = NULL ;
}
Node *build( int lf , int rg ){
Node *nd ; newNode( nd ) ;
if( lf != rg ){
int mid = ( lf + rg ) >> 1 ;
nd->ch[0] = build( lf , mid ) ;
nd->ch[1] = build( mid+1,rg ) ;
In( nd->id , nd->ch[0]->id , 1e7 , 0 ) ;
In( nd->id , nd->ch[1]->id , 1e7 , 0 ) ;
} else In( nd->id , T , 1 , 0 ) ;
return nd ;
}
void addE( Node *nd , int lf , int rg , int L , int R , int id ){
if( L <= lf && rg <= R ){
In( id , nd->id , 1 , 0 ) ;
return ;
} else {
int mid = ( lf + rg ) >> 1 ;
if( L <= mid ) addE( nd->ch[0] , lf , mid , L , R , id ) ;
if( R > mid ) addE( nd->ch[1] , mid+1,rg , L , R , id ) ;
}
}
deque<int> que ;
bool inque[5000*15] ;
int dis[5000*15] , pre[5000*15] , preE[5000*15] ;
bool Spfa(){
memset( pre + 1 , 0 , id_c * sizeof( int ) ) ;
memset( dis + 1 , 0 , id_c * sizeof( int ) ) ;
dis[S] = 0 , que.push_front( S ) , inque[S] = true ;
while( !que.empty() ){
int u = que.front() ;
que.pop_front() ; inque[u] = false ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( p[i].flow && dis[v] < dis[u] + p[i].fee ){
dis[v] = dis[u] + p[i].fee ;
pre[v] = u , preE[v] = i ;
if( !inque[v] ){
if( !que.empty() && dis[v] >= dis[ que.front() ] ) que.push_front( v ) ;
else que.push_back( v ) ;
inque[v] = true ;
}
}
}
} return pre[T] ;
}
int addFlow(){
// puts( " ----------------------- " ) ;
int flow = 0x3f3f3f3f , rt = 0 , now = T ;
while( now != S ){
flow = min( flow , p[ preE[now] ].flow ) ;
now = pre[now] ;
} now = T ;
while( now != S ){
// printf( "now = %d\n" , now ) ;
p[ preE[now] ].flow -= flow ;
p[ preE[now]^1 ].flow += flow ;
rt += p[ preE[now] ].fee * flow ;
now = pre[now] ;
} return rt ;
}
void solve(){
while( Spfa() )
ans += addFlow() ;
printf( "%d" , ans ) ;
}
int main(){
scanf( "%d" , &N ) ;
for( int i = 1 ; i <= N ; i ++ ) rob[i] = ++id_c ;
S = ++id_c , T = ++id_c ;
for( int i = 1 ; i <= N ; i ++ ){
scanf( "%d%d%d" , &d[i].a , &d[i].b , &d[i].c ) ; d[i].b -- ;
In( S , rob[i] , 1 , d[i].c ) ;
mint = min( d[i].a , mint ) ;
maxt = max( d[i].b , maxt ) ;
}
root = build( mint , maxt ) ;
for( int i = 1 ; i <= N ; i ++ )
addE( root , mint , maxt , d[i].a , d[i].b , rob[i] ) ;
solve() ;
}
BZOJ2034
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
map<int,int> mp ;
int N , pos[5005] , match[5005] ;
struct Data{
int st , ed , v ;
}d[5005] ;
bool cmp_st( const Data &A , const Data &B ){ return A.st < B.st ; }
bool cmp_v ( const Data &A , const Data &B ){ return A.v > B.v ; }
bool matching( int u , int p ){
if( p > N || d[u].ed < pos[p] ) return false ;
if( !match[p] ){
match[p] = u ;
return true ;
} else {
if( d[ match[p] ].ed < d[u].ed ) return matching( u , p + 1 ) ;
else if( matching( match[p] , p + 1 ) ){
match[p] = u ;
return true ;
}
} return false ;
}
void solve(){
long long ans = 0 ;
sort( d + 1 , d + N + 1 , cmp_v ) ;
for( int i = 1 ; i <= N ; i ++ )
ans += 1LL * matching( i , mp[ d[i].st ] ) * d[i].v ;
printf( "%lld" , ans ) ;
}
int main(){
scanf( "%d" , &N ) ;
for( int i = 1 ; i <= N ; i ++ )
scanf( "%d%d%d" , &d[i].st , &d[i].ed , &d[i].v ) ;
sort( d + 1 , d + N + 1 , cmp_st ) ;
for( int i = 1 ; i <= N ; i ++ ){
pos[i] = max( pos[i-1] + 1 , d[i].st ) ;
if( !mp.count( pos[i] ) )
mp[ pos[i] ] = i ;
}
solve() ;
}