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;
}
posted @ 2021-05-20 16:33  cminus  阅读(129)  评论(0编辑  收藏  举报