隐藏
「SHOI2003」吃豆豆 - 拆点+费用流 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「SHOI2003」吃豆豆 - 拆点+费用流

题目大意


题目分析

发现题目的不相交没什么用,因为如果相交可以交换路径变成不相交。

每个点拆成两个点,一个入点一个出点,入点向出点连一条容量为$1$费用为$1$的边,再向出点连一条容量为$1$费用为$0$的边,每个出点向比它横纵坐标都大的入点连容量为$2$费用为$0$的边,跑最大费用最大流即可。

这样建边会超时。
优化一下,每个出点仅向第一个比它大的入点连边。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

const int maxn=4005;

struct Edge {
int from,to,cap,flow,cost;
Edge(int x=0,int y=0,int c=0,int f=0,int co=0):from(x),to(y),cap(c),flow(f),cost(co) {}
};

struct zkw_CostFlow {
int n,m,s,t;
vector<Edge>edges;
vector<int>G[maxn];
bool inque[maxn],vst[maxn];
int dist[maxn];
int flow,cost;
void init(int n) {
this->n=n;
edges.clear();
for(int i=1; i<=n; i++)G[i].clear();
}
void AddEdge(int x,int y,int v,int f) {
edges.push_back(Edge(x,y,v,0,f));
edges.push_back(Edge(y,x,0,0,-f));
m=edges.size();
G[x].push_back(m-2);
G[y].push_back(m-1);
}
bool spfa() {
for(int i=1; i<=n; i++)dist[i]=-INT_MAX;
memset(inque,0,sizeof(inque));
dist[t]=0;
inque[t]=1;
deque<int>Q;
Q.push_back(t);
while(!Q.empty()) {
int Now=Q.front();
Q.pop_front();
inque[Now]=0;
for(int id:G[Now]) {
Edge& e=edges[id^1];
int Next=e.from;
if(dist[Next]<dist[Now]+e.cost&&e.cap>e.flow) {
dist[Next]=dist[Now]+e.cost;
if(!inque[Next]) {
inque[Next]=1;
if(!Q.empty()&&dist[Next]>dist[Q.front()])Q.push_front(Next);
else Q.push_back(Next);
}
}
}
}
return dist[s]>-INT_MAX;
}
int dfs(int Now,int a) {
if(vst[Now])return 0;
if(Now==t||a==0)return a;
int flow=0;
vst[Now]=1;
for(int id:G[Now]) {
Edge& e=edges[id];
int Next=e.to;
if(dist[Now]-e.cost!=dist[Next])continue;
int nextflow=dfs(Next,min(a,e.cap-e.flow));
if(nextflow>0) {
cost+=nextflow*e.cost;
e.flow+=nextflow;
edges[id^1].flow-=nextflow;
flow+=nextflow;
a-=nextflow;
if(a==0)break;
}
}
return flow;
}
int maxflow(int s,int t) {
this->s=s;
this->t=t;
flow=cost=0;
while(spfa()) {
memset(vst,0,sizeof(vst));
flow+=dfs(s,INT_MAX);
}
return flow;
}
} zkw;

struct Point {
int x,y;
bool operator < (const Point& b) const {
return x<b.x||(x==b.x&&y<b.y);
}
} p[maxn];

int n;

int main() {
n=Get_Int();
for(int i=1; i<=n; i++) {
p[i].x=Get_Int();
p[i].y=Get_Int();
}
sort(p+1,p+n+1);
int realS=2*n+1,T=2*n+2,fakeS=2*n+3;
zkw.init(fakeS);
for(int i=1; i<=n; i++) {
int Max=INT_MAX;
for(int j=i+1; j<=n; j++) {
if(p[j].y>=Max||p[j].y<p[i].y)continue;
Max=p[j].y;
zkw.AddEdge(n+i,j,2,0);
}
}
for(int i=1; i<=n; i++) {
zkw.AddEdge(i,n+i,1,1);
zkw.AddEdge(i,n+i,1,0);
zkw.AddEdge(n+i,T,2,0);
zkw.AddEdge(fakeS,i,2,0);
}
zkw.AddEdge(realS,fakeS,2,0);
zkw.maxflow(realS,T);
printf("%d\n",zkw.cost);
return 0;
}
姥爷们赏瓶冰阔落吧~