spoj 3196 Divisibility Relation(最大独立集输出方案)

题意:给出200个数,选出最大的子集,使得子集中的数相互不整除。

解法:将可以整除的点连边,看上去像是个无向图最大独立集,通过求最大团的办法求解复杂度hold不住。于是观察图的特点,由于整除是一种偏序关系,因此构成的是一个有向无环图,因此也是个二分图(二分图的一个充分必要条件是无奇圈),于是可以转化为求解二分图的最大独立集。具体做法Matrix67给出了证明过程,这篇文章里有http://blog.csdn.net/kksleric/article/details/7465804

public class Main {
	int maxn = 210, maxm = 40010;
	class node {
		int be, ne;
		node(int b, int e) {
			be = b;
			ne = e;
		}
	}
	class Edmonds {
		int E[] = new int[maxn], n, m, len;
		node buf[] = new node[maxm];
		int link[] = new int[maxn];
		boolean vis[] = new boolean[maxn];
		void init(int n, int m) {
			this.m = m;
			this.n = n;
			len = 0;
			Arrays.fill(E, -1);
		}
		void add(int a, int b) {
			buf[len] = new node(b, E[a]);
			E[a] = len++;
		}
		boolean find(int a) {
			for (int i = E[a]; i != -1; i = buf[i].ne) {
				int b = buf[i].be;
				if (vis[b])
					continue;
				vis[b] = true;
				if (link[b] == -1 || find(link[b])) {
					link[b] = a;
					return true;
				}
			}
			return false;
		}
		int solve() {
			Arrays.fill(link, -1);
			int ans = 0;
			for (int i = 1; i <= n; i++) {
				Arrays.fill(vis, false);
				if (find(i))
					ans++;
			}
			return ans;
		}

		boolean cv[] = new boolean[maxn + maxn];
		void cover() {// 求解最小覆盖集
			Arrays.fill(cv, false);
			for (int i = 1; i <= m; i++)
				if (link[i] == -1)
					cv[n + i] = true;
			boolean end = false;
			while (!end) {
				end = true;
				for (int i = 1; i <= n; i++) {
					if (cv[i])
						continue;
					boolean flag = false;
					for (int j = E[i]; j != -1; j = buf[j].ne) {
						int b = buf[j].be;
						flag |= cv[b + n];
					}
					if (!flag)
						continue;
					cv[i] = true;
					end = false;
					for (int j = 1; j <= m; j++)
						if (link[j] == i)
							cv[j + n] = true;
				}
			}
			for (int i = 1; i <= m; i++)
				cv[i + n] = !cv[i + n];
		}
	}
	Scanner scan = new Scanner(System.in);
	Edmonds hun = new Edmonds();
	int arr[] = new int[maxn], ans[] = new int[maxn];
	void run() {
		int n = scan.nextInt();
		int m = scan.nextInt();
		hun.init(n, n);
		while (m-- > 0) {
			int a = scan.nextInt();
			int b = scan.nextInt();
			hun.add(Math.min(a, b), Math.max(a, b));
		}
		int res = n - hun.solve();
		System.out.println(res);
		hun.cover();
		for (int i = 1; i <= n; i++)
			if (!hun.cv[i] && !hun.cv[i + n]) {
				if (--res > 0)
					System.out.print(i+ " ");
				else
					System.out.println(i);
			}
	}
	public static void main(String[] args) throws IOException {
		new Main().run();
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值