2019湘潭邀请赛A - Chessboard(HDU-6532)
看到题目的时候就感觉是一个费用流
但是怎么想都想不出来怎么做(太菜了)
最大的问题大概是我把这个网格看成了若干个点,而没有思考它们在行列上的关系
对于两个R限制,每次一定有一个限制是基于前一个限制的,例如R 1 10和R 3 5,3>1,所以3行及之后的点一定是先满足前一个条件,再满足这一个条件,基于此,我们把点按照行排序,向第一个被限制的点从s连上容量为k的边,然后再依排序顺序一个个连其他的所有受限制的边,容量为inf,对于下一个限制,找到第一个被限制的点,从上一个点到这个点的容量改成k,这样就可以做到限制每一行的效果。
但是,还有一个列的要求,当然我觉得应该可以再按照列排序,然后用刚刚的方法连接(未验证)。分析连边的特点,是按照行或者列的顺序连边的,所以我们可以先离散化坐标,每个方格就用它所在的行和列之间的连边来表示,刚刚的边就转换成了行与行和列与列之间的连边,用费用列求解即可
需要注意的是,列是要倒着连边的,以为列流向汇点
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
typedef long long ll;
struct Edge {
int from, to, w, cap, flow;
};
const int INF = 0x3f3f3f3f;
struct MCMF {
vector < Edge > edge;
vector < int > G[N];
bool vis[N];
int cur[N], d[N], s, t;
ll ret = 0;
inline void Add(int from, int to, int w, int cap){
edge.push_back((Edge){from, to, w, cap, 0});
edge.push_back((Edge){to, from, -w, 0, 0}); //注意这里,w是相反的数
int m = edge.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
inline bool SPFA(){
memset(d, 0x3f, sizeof(d));
queue < int > q;
q.push(s);
d[s] = 0;
vis[s] = 1;
while(!q.empty()){
int u = q.front(); q.pop();
vis[u] = false;
for (int i = 0; i < G[u].size(); i++) {
Edge& e = edge[G[u][i]];
if (d[e.to] > d[u] + e.w && e.cap > e.flow) {
d[e.to] = d[u] + e.w;
if (!vis[e.to]) q.push(e.to), vis[e.to] = true;
}
}
}
return d[t] != INF;
}
int dfs(int x, int a){
if (x == t || a == 0) return a;
int flow = 0, f; vis[x] = true; //因为可能有0边,不像单纯的最大流能保证是+1
for (int& i = cur[x]; i < G[x].size(); i++) {
Edge& e = edge[G[x][i]];
if (!vis[e.to] && d[x] + e.w == d[e.to] && (f = dfs(e.to, min(e.cap - e.flow, a))) > 0){
e.flow += f;
edge[G[x][i] ^ 1].flow -= f;
flow += f;
a -= f; ret += 1ll * e.w * f;
if (a == 0) break;
}
}
vis[x] = false;
return flow;
}
inline int mcmf(){
int ans = 0;
while(SPFA()){
memset(cur, 0, sizeof(cur));
ans += dfs(s, INF);
}
return ans;
}
}D;
int a[N], b[N], x[N], y[N];
int minn[2][N];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &x[i], &y[i]);
a[i] = x[i]; b[i] = y[i];
}
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
int newx = unique(a + 1, a + n + 1) - a - 1;
int newy = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) {
int p1 = lower_bound(a + 1, a + newx + 1, x[i]) - a;
int p2 = lower_bound(b + 1, b + newy + 1, y[i]) - b;
D.Add(p1, p2 + n + 1, -i, 1);
}
int m;
scanf("%d", &m);
memset(minn, 0x3f, sizeof(minn));
for (int i = 1; i <= m; i++) {
char s[3];
int j, k;
scanf("%s%d%d", s, &j, &k);
if (s[0] == 'R') {
int p = lower_bound(a + 1, a + newx + 1, j) - a;
minn[0][p] = min(k, minn[0][p]);
}
else {
int p = lower_bound(b + 1, b + newy + 1, j) - b;
minn[1][p] = min(k, minn[0][p]);
}
}
for (int i = 1; i <= newx || i <= newy; i++) {
if (i <= newx)
D.Add(i - 1, i, 0, minn[0][i]);
if (i <= newy)
D.Add(i + n + 1, i + n, 0, minn[1][i]);
}
D.s = 0; D.t = n + 1;
D.mcmf();
printf("%lld\n", -D.ret);
return 0;
}