您的位置:首页 > 其它

Bzoj4276 [ONTAK2015]Bajtman i Okrągły Robin

2017-05-24 15:24 429 查看
原题网址:http://www.lydsy.com/JudgeOnline/problem.php?id=4276

一开始想在每个时间点贪心,为了正确性,加了一贯贪心的撤销操作,当当前时间点权值最大的能取的因为太晚而取不了就找一个前面能和他交换时间的互换抢劫时间,但WA掉了,然后问了神犇,他提到我这样只能一步交换,有可能连环交换,而且另一个神犇提到了网络流贪心做多半是有退流的问题。 附上WA的贪心代码:

#include<bits/stdc++.h>
const int N = 5050;
struct rec{int a, b, c;};
rec a
,b
,heap
;
int n,cnt,tot,ans;
bool cmp(const rec &a, const rec &b){
return a.a < b.a;
}
void heapup(int x){
while (x > 1 && heap[x].c > heap[x >> 1].c)
std::swap(heap[x >> 1],heap[x]),
x >>= 1;
}
void heapdown(int x){
while (x*2 <= cnt && heap[x*2].c > heap[x].c || x*2+1 <= cnt && heap[x*2+1].c > heap[x].c)
if (x*2+1 <= cnt && heap[x*2+1].c > heap[x*2].c)
std::swap(heap[x*2+1],heap[x]), x = x*2+1;
else
std::swap(heap[x*2],heap[x]), x = x*2;
}
void push(const rec &x){
heap[++cnt] = x;
heapup(cnt);
}
void poop(){
heap[1] = heap[cnt--];
heapdown(1);
}
int main(){
scanf("%d",&n);
for (int i=1; i<=n; i++)
scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),
a[i].b--;
std::sort(a+1,a+n+1,cmp);
int p = 1;
for (int tim=1; tim<5000; tim++){
for (; p <= n && a[p].a == tim; p++)
push(a[p]);
bool picked = 0;
for (; !picked && cnt; poop())
if (heap[1].b >= tim){
b[++tot] = heap[1];
b[tot].a = tim + 1;
ans += heap[1].c;
picked = 1;
}else
for (int i=1; i<=tot; i++)
if (b[i].a <= tim && b[i].b >= tim){
b[i].a = tim + 1;
ans += heap[1].c;
picked = 1;
break;
}
}
printf("%d\n",ans);
return 0;
}


正解实际是费用流,实质是一个二分图最大权匹配,一边是强盗,另一边是日期,特殊的是因为匹配的是一个区间内的日期,所以可以用线段树优化建图。

#include<bits/stdc++.h>
const int N = 15001;
const int M = 3e5;
const int INF = 1e9;
struct edge{int x,y,f,v,next;} mp[M];
int n,S,T,s,a,b,c,cnt,ans,first
,q[N + 10],lc
,rc
,dis
,from
;
bool inq
;
void ins(int x, int y, int f, int v){
mp[++s] = (edge){x,y,f,v,first[x]}; first[x] = s;
mp[++s] = (edge){y,x,0,-v,first[y]}; first[y] = s;
}
void build(int i, int l, int r){
if (l == r) {ins(i,T,1,0); return;}
ins(i,lc[i] = ++cnt,INF,0);
build(cnt,l,(l+r)/2);
ins(i,rc[i] = ++cnt,INF,0);
build(cnt,(l+r)/2+1,r);
}
void find(int k, int i, int l, int r, int _l, int _r){
if (l > _r || r < _l) return;
if (l >= _l && r <= _r) {ins(k,i,1,0); return;}
find(k,lc[i],l,(l+r)/2,_l,_r);
find(k,rc[i],(l+r)/2+1,r,_l,_r);
}
bool SPFA(){
int head = 1, tail = 2;
memset(inq,0,sizeof(inq));
for (int i=S; i<=T; i++) dis[i] = -INF;
dis[q[head] = S] = 0;
inq[q[head]] = 1;
while (head != tail){
int x = q[head++];
if (head > N) head = 1;
for (int t=first[x]; t>0; t=mp[t].next){
int y = mp[t].y;
if (mp[t].f && dis[x] + mp[t].v > dis[y]){
dis[y] = dis[x] + mp[t].v;
from[y] = t;
if (!inq[y]){
inq[q[tail++] = y] = 1;
if (tail > N) tail = 1;
}
}
}
inq[x] = 0;
}
return dis[T] > -INF;
}
void mcf(){
for (int x=T; x!=S; x=mp[from[x]].x)
mp[from[x]].f--,
mp[from[x]^1].f++;
ans += dis[T];
}
int main(){
scanf("%d",&n);
S = 0; T = 15000; s = 1;
build(cnt = n + 1,1,5000);
for (int i=1; i<=n; i++)
scanf("%d%d%d",&a,&b,&c),
ins(S,i,1,c),
find(i,n+1,1,5000,a,b-1);
while (SPFA()) mcf();
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  费用流