[贪心 + 分组背包]圆覆盖问题

一个包含n个圆心,每个圆半径都为r的位置坐标文件,你设计一个算法,找出m个圆心,使这m个圆总覆盖区域最大。

换个意思就是选择m个交集最小的圆

step1: 构建图:圆心为节点,有交集添边,边权为交集面积

step2: 简单bfs算法。目的把图分割为一个个连通图

step3: 直接选择孤立的节点.也就是不相交的节点。假设选了L个

step4:  若L >= m。则返回前m个圆心

step5: 把每组连通图贪心的删除节点。如包含p个节点的连通图,删除和其它圆交集最大圆,构成一个p-1个节点的子图。这个子图再删除一个相交最大圆,构成p-2个节点的子图。以此类推。 这些子图组成一组,体积v为节点个数,代价w为交集面积。

step6: 分组背包:背包体积V=m-L,按照step5分好的组装入背包。记录状态转移过程。对于每个w修改为max_w - w。

我们先来看分组背包问题

有N件物品和一个容量为V的背包,第i件物品的重量为c[i],价值为w[i],这些物品被划分成了若干组,每组中的物品互相冲突,最多选一件

问将哪些物品放入背包中可以使背包获得最大的价值

我们用f[k][v]表示前k种物品花费费用v所能取得的最大价值

给出状态转移方程:

f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于第k组}

实现:  

//注意这里是三层循环
for 所有的组k
  for v=V to 0
    for 所有属于组k的i
      f[v]=max{f[v],f[v-c[i]]+w[i]}
    end
  end
end

选择的节点就是孤立节点+分组背包选择的节点。

极限情况下,可能会 连通图很大,没有孤立节点,这样V就会很大,每一组的遍历也会很多。step5应该可以优化一下。优化情况有空再想想。

python代码实现:

import numpy as np
import math

def load_data(path):
	data = np.loadtxt(path,dtype=np.float,delimiter=",")
	return data
def intersection(a,b,r):
	p = a - b
	d = math.hypot(p[0],p[1])
	if d == 0:
		return np.pi*r*r 
	if  d < 2*r:
		ang=2*np.arccos((r*r+d*d-r*r)/(2*r*d));
		s = ang*r*r - r*d*np.sin(ang)
		return s
	return 0
def bfs(seed, adj, visited):
	group = [seed]
	que = [seed]
	S = 0
	while len(que)>0:
		f = que.pop(0)
		for neighbor,s in adj[f]:
			if visited[neighbor] != 1:
				visited[neighbor] = 1
				group.append(neighbor)
				S += s
				que.append(neighbor)
	return group, S
def subGroup(group,adj):
	sloss = []
	for u in group:
		sloss.append((u,np.sum([s for v,s in adj[u]])))
	sloss.sort(key=lambda x:x[1], reverse=True)
	def getLoss(nodes, adj):
		rets = 0
		for u in nodes:
			for v,s in adj[u]:
				if v in nodes:
					rets += s
		return rets/2
	retGroups = []
	for i in range(0,len(sloss)):
		nodes = [v for v,s in sloss[i:]]
		retGroups.append([nodes, getLoss(nodes, adj)])
	return retGroups
def DP(gs, V):
	T = len(gs)
	maxS = -1
	for i in range(0,T):
		for g,s in gs[i]:
			maxS = max(maxS,s)
	for i in range(0,T):
		for k in range(0,len(gs[i])):
			gs[i][k][1] = maxS - gs[i][k][1]
	dps = np.zeros(V+1)
	pSet = {i:[] for i in range(V+1)}
	for i in range(T):
		for j in range(V,0,-1):
			choiced = None
			for k in range(len(gs[i])):
				v = len(gs[i][k][0])
				w = gs[i][k][1]
				if j - v >= 0:
					comp = dps[j - v] + w
					if dps[j]< comp:
						choiced = k
						dps[j] = comp
			pSet[j].append(choiced)
	retNodes = []
	for i in range(T):
		num = pSet[V][i]
		if num != None:
			retNodes.extend(gs[i][num][0])
	return retNodes




def solution(data,r,n):
	adj = {i:[] for i in range(data.shape[0])}
	for i in range(data.shape[0]):
		for j in range(i+1,data.shape[0]):
			s = intersection(data[i,:],data[j,:],r)
			if s>0:
				adj[i].append((j,s))
				adj[j].append((i,s))
	visited = np.zeros(data.shape[0],dtype = np.int)
	groups = []
	for i in range(data.shape[0]):
		if visited[i] == 0:
			visited[i] = 1
			group, S = bfs(i,adj,visited)
			groups.append((group,S))
	groups.sort(key=lambda x:x[1])
	choiced = [groups[i][0][0] for i in range(len(groups)) if len(groups[i][0])==1]
	choiced_n = len(choiced)
	if choiced_n >= n:
		return choiced[:n]
	ngroups = []
	for i in range(choiced_n,len(groups)):
		group = groups[i]
		ngroups.append(subGroup(group[0], adj))
	retDP = DP(ngroups,n-choiced_n)
	choiced.extend(retDP)

	return choiced


from matplotlib.patches import Ellipse, Circle
import matplotlib.pyplot as plt
def visualization(nodes, coordinates, r):
	fig=plt.figure(1)
	xmax = None
	xmin = None
	ymax = None
	ymin = None
	for x,y in coordinates:
		xmax = x if xmax==None or x > xmax else xmax
		xmin = x if xmin==None or x < xmin else xmin
		ymax = y if ymax==None or y > ymax else ymax
		ymin = y if ymin==None or y < ymin else ymin
	plt.axis([xmin,xmax,ymin,ymax])
	ax=fig.add_subplot(1,1,1)
	nSet = set(nodes)
	for i in range(coordinates.shape[0]):
		x,y = coordinates[i]
		if i in nSet:
			cir = Circle(xy = (x, y), radius=r, color='r', alpha=0.5)
		else:
			cir = Circle(xy = (x, y), radius=r, color='g', alpha=0.5)
		ax.add_patch(cir)

	plt.axis('scaled')
	plt.show()

if __name__ == "__main__":
    data = load_data('./GPS.csv')
    r = float(input("input r:"))
    n = int(input("how many nodes would you want to choice? input n:"))
    if n > data.shape[0]:
    	print("too many n!")
    nodes = solution(data,r,n)
    visualization(nodes,data,r)

 可视化看一看:

红色为选择,绿色不选。

[贪心 + 分组背包]圆覆盖问题_第1张图片

可以看到我们选择的红色圆都躲避了“高密度”地区。

测试数据的话。自己可以用随机数生成一下。

 

你可能感兴趣的:(acm_dp,动态规划,圆覆盖问题,dp)