问题描述
有一个无向图(V,E),边上的权重w:E→N,a 目标k∈N,阈值O∈N。找到该k个顶点的树 图的重量小于阈值。换句话说,选择k V和E的顶点和k-1边分别使它们 构成一棵树,并且所选边的权重之和 小于O。
编写一个以V,E,w,k和O为输入的ASP程序,并找到 选择满足约束的边或输出 如果无法满足约束条件,则为“无法满足”。选择 边隐式地诱发了顶点的选择,因此没有 需要明确显示选定的顶点。
谓词vertex / 1为该问题提供了一个实例, 权重/ 3,目标/ 1和阈值/ 1。所有边缘都有权重,所以 形式为weight(a,b,10)的语句。可以用来声明 在顶点a和b之间同时存在一条边 声明其重量,并且不需要任何多余的边/ 2 谓词。
我尝试了以下操作:
% instance
vertex ( v1 ). vertex ( v2 ). vertex ( v3 ).
vertex ( v4 ). vertex ( v5 ). vertex ( v6 ).
vertex ( v7 ). vertex ( v8 ). vertex ( v9 ).
weight ( v1,v2,3). weight ( v1,v3,3).
weight ( v2,v4,1). weight ( v2,v5,5).
weight ( v3,3). weight ( v3,v6,4).
weight ( v4,4). weight ( v4,v7,1).
weight ( v5,7).
weight ( v6,2). weight ( v6,v8,2).
weight ( v7,v9,3).
weight ( v8,2).
target (4).
threshold (4).
% encoding
(P-1) {select(X,Y) : weight(X,Y,Z)} (Q-1) :- target(P),target(Q).
sum(S) :- S = #sum {W,X,Y : select(X,Y),weight(X,W); W,Z : select(X,Z),Z,W) }.
:- sum(S),threshold(M),S > M.
:- select(A,B),select(C,D),A == C ; A == D ; B == C ; B == D.
#show select/2.
我得到以下输出:
clingo version 5.5.0
Reading from stdin
Solving...
Answer: 1
select(v2,v4) select(v4,v7) select(v6,v7)
Answer: 2
select(v2,v8)
Answer: 3
select(v2,v7) select(v8,v9)
SATISFIABLE
Models : 3
Calls : 1
Time : 0.013s (Solving: 0.00s 1st Model: 0.00s Unsat: 0.00s)
cpu Time : 0.000s
我只是期待
select(v2,v7)
因为其他人显然不是发束人。
我认为这是因为问题所在:
:- select(A,A == C ; A == D ; B == C ; B == D.
我该如何纠正?
解决方法
好的,那很复杂。我很确定我的解决方案并不完美,我也是一个初学者。
在开始编写代码之前,让我们再次检查问题:要求是选择k
顶点和k-1
边。如果稍微考虑一下,它可以恰好形成两种模式:一棵相连的树或多个至少有一个周期的非相连图。因此,如果您确定没有循环,您会得到一棵相连的树。
我在事实上添加了一些顶点,以检查是否形成了树或是否找到了便宜的未连接循环,为此,我不得不将target
和threshold
更改为更高的值。 / p>
#const n = 5.
vertex ( v1; v2; v3; v4; v5; v6; v7; v8; v9 ).
vertex ( m1; m2; m3 ).
weight ( v1,v2,3). weight ( v1,v3,3).
weight ( v2,v4,1). weight ( v2,v5,5).
weight ( v3,3). weight ( v3,v6,4).
weight ( v4,4). weight ( v4,v7,1).
weight ( v5,7).
weight ( v6,2). weight ( v6,v8,2).
weight ( v7,v9,3).
weight ( v8,2).
weight ( m1,m2,0).
weight ( m2,m3,0).
weight ( m3,m1,0).
target (n).
threshold (6).
现在是代码,后面有解释。
% select subset of nodes and vertices
(P) {select(X) : vertex(X)} (P) :- target(P).
(P-1) {select(X,Y) : weight(X,Y,Z)} (Q-1) :- target(P),target(Q).
% postion does not matter in an undirected graph.
directed(A,B):-select(A,B).
directed(B,A):-select(A,B).
% for every selected edge all nodes are selected
:- directed(A,_),vertex(A),not select(A).
% for every selected node there exists at least one edge
:- select(A),{directed(A,B):vertex(B)}0.
% select a direction for each selected edge
{dir(A,B);dir(B,A)}==1 :- select(A,B).
% force them in an order
{ found(X,1..n) } == 1 :- select(X).
{ found(X,N):select(X) } == 1 :- N = 1..n.
% reject if one edge does not follow the order
:- found(X,NX),found(Y,NY),dir(X,Y),NY<NX.
% reject if 2 different edges end in the same vertex
:- dir(X,Z),dir(Y,X!=Y.
sum(S) :- S = #sum {W,X,Y : select(X,weight(X,W); W,Z : select(X,Z,W) }.
:- sum(S),threshold(M),S > M.
#show select/2.
说明:
- 为了方便我,我在
select/1
谓词中添加了选定的顶点。 - 由于处理无向图总是必须检查两个位置,因此我添加了
directed/2
谓词,这是所选边的有向图版本。 - 接下来,我确保每个选定的顶点都有选定的边,反之亦然。
- 现在出现了复杂的部分:检测周期。为此,我使用谓词
dir/2
在每个选定的边沿的两个方向之一上施压。在有向图中测试树比较容易。 - 接下来,我向顶点下订单
found/2
。有向边dir/2
仅按照该顺序排列。这会迫使周期变为某种行为。 - 现在是循环破坏者:如果所选图形具有循环,则
dir/2
的两条边将在同一顶点结束。拒绝。如果这只是clingo的不幸猜测,那么它将发现一个更幸运的猜测,可以满足这个标准。 - 总和的计算是从您那里复制并粘贴的。
输出为16倍
select(v2,v4) select(v4,v7) select(v6,v8)
重复项来自found/2
中顶点的顺序可以不同但仍得到相同结果的事实。