• 洛谷 P4258 UOJ #171 【WC2016】挑战NPC


    题解

    建模:

    把每个框子拆成三个点并连在一起,每颗球向他能匹配的框子的三个点分别连边。
    之后求一般图最大匹配即可

    证明:

    如果原问题取得最优解,每颗球都会与一个框匹配,每个框如果匹配的球数不超过两个,则内部产生一条匹配边,这是一个最大匹配。
    反过来,任意最大匹配中,每颗球必然能匹配,而后框子的“内部匹配边”会尽可能多,所以任意最大匹配一定是如上所述的一个匹配。

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int maxm = 100 + 5;
    const int maxn = maxm * 6;
    const int maxe = 500000 + 5;
    
    int t;
    int n, m, e;
    int head[maxn], nxt[maxe * 2], to[maxe * 2], cnt;
    void inline addEdge(int u, int v) {
    	nxt[++cnt] = head[u], to[cnt] = v, head[u] = cnt;
    	nxt[++cnt] = head[v], to[cnt] = u, head[v] = cnt;
    }
    
    namespace Blossom
    {
    	int pre[maxn];
    	int find(int x) { return pre[x] ? pre[x] = find(pre[x]) : x; }
    	int type[maxn], que[maxn], front, tail, link[maxn], match[maxn], deep[maxn], fa[maxn];
    	
    	int inline lca(int u, int v) {
    		u = find(u), v = find(v);
    		while(u != v) {
    			if(deep[u] > deep[v]) u = find(fa[u]);
    			else v = find(fa[v]);
    		}
    		return u;
    	}
    	
    	void inline shrink(int u, int v, int e) {
    		while(find(u) != e) {
    			link[u] = v, v = match[u];
    			if(type[v] == 2) type[v] = 1, que[tail++] = v;
    			if(find(u) == u) pre[u] = e;
    			if(find(v) == v) pre[v] = e;
    			u = link[v];
    		}
    	}
    	
    	bool inline bfs(int s)
    	{
    		memset(pre, 0, sizeof pre); 
    		memset(type, 0, sizeof type); 
    		memset(link, 0, sizeof fa);
    		memset(fa, 0, sizeof fa);
    		front = 0, tail = 0;
    		type[que[tail++] = s] = 1;
    		deep[s] = 1; fa[s] = 0;
    		while(front != tail) 
    		{
    			int u = que[front++];
    			for(int e = head[u]; e; e = nxt[e]) 
    			{
    				int v = to[e];
    				if(find(v) == find(u) || type[v] == 2) continue;
    				if(!type[v]) {
    					deep[v] = deep[u] + 1, fa[v] = u;
    					type[v] = 2, link[v] = u;
    					if(!match[v]) {
    						for(int now = v, last; now; now = last) {
    							last = match[link[now]];
    							match[now] = link[now], match[link[now]] = now;
    						}
    						return true;
    					}
    					type[match[v]] = 1, que[tail++] = match[v]; deep[match[v]] = deep[v] + 1, fa[match[v]] = v;
    				} else if(type[v] == 1){
    					int e = lca(u, v);
    					shrink(u, v, e);
    					shrink(v, u, e);
    				}
    			}
    		}
    		
    		return false;
    	}
    	
    	int inline edmond() {
    		int ans = 0;
    		memset(match, 0, sizeof match);
    		for(int i = 1; i <= n + 3 * m; ++i) if(!match[i]) ans += bfs(i);
    		return ans;
    	}
    }
    
    using namespace Blossom;
    
    void inline Init()
    {
    	memset(head, 0, sizeof head);
    	memset(to, 0, sizeof to);
    	memset(nxt, 0, sizeof nxt);
    	cnt = 0;
    	scanf("%d %d %d", &n, &m, &e);
    	int u, v;
    	while(e--) {
    		scanf("%d %d", &u, &v);
    		addEdge(u, 3 * (v - 1) + n + 1);
    		addEdge(u, 3 * (v - 1) + n + 2);
    		addEdge(u, 3 * (v - 1) + n + 3);
    	}
    	for(int i = 1; i <= m; ++i) {
    		int a = 3 * (i - 1) + n + 1, b = 3 * (i - 1) + n + 2, c = 3 * (i - 1) + n + 3;
    		addEdge(a, b);
    		addEdge(b, c);
    		addEdge(c, a);
    	}
    }
    
    int main()
    {
    	scanf("%d
    ", &t);
    	while(t--) {
    		Init();
    		printf("%d
    ", edmond() - n);
    		for(int i = 1; i <= n; ++i) printf("%d ", (match[i] - n - 1) / 3 + 1);
    		putchar('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    kafka 生产者消费者 api接口
    湖南省第九届大学生计算机程序设计竞赛 Interesting Calculator
    Debugger DataSet 调试时查看DataSet
    DELPHI 常用虚拟键:VK_
    DBGRID控件里可以实现SHIFT复选吗?怎么设置?
    在dbgrid中如何多行选中记录(ctl与shift均可用)
    如何在DBGrid里实现Shift+“选择行”区间多选的功能!
    按着shift键对dbgrid进行多条记录选择的问题(50分)
    Delphi实现DBGrid Shift+鼠标左键单击 多选
    Delphi定位TDataSet数据集最后一条记录
  • 原文地址:https://www.cnblogs.com/cjrsacred/p/10166455.html
Copyright © 2020-2023  润新知