d[u][j]表示从以u为根的子树中获得j张选票时的最小花费
转移方程为:d[u][j] = min(sigema(d[vi][ji]));设u有n个儿子v1...vn,这里表示选取所有j1+...+jn=j的组合中有最小花费的组合
方程虽然暴力,但是很好懂很好实现
#include
<
iostream
>
#include
<
vector
>
#include
<
map
>
#include
<
string
>
using
namespace
std;
#define
MAXN 210
#define
INF 0x7f7f7f7f
int
d[MAXN][MAXN], n, m, c[MAXN], cnt, nodnum[MAXN];
int
indeg[MAXN];
vector
<
int
>
son[MAXN];
map
<
string
,
int
>
mp;
void
init(){
int
i;
cnt
=
1
;
memset(d,
0x7f
,
sizeof
(d));
mp.clear();
for
(i
=
0
; i
<=
n; i
++
)
son[i].clear();
memset(indeg,
0
,
sizeof
(indeg));
}
int
input(
string
name){
map
<
string
,
int
>
::iterator it
=
mp.find(name);
if
(it
!=
mp.end())
return
it
->
second;
mp[name]
=
cnt
++
;
return
cnt
-
1
;
}
int
calnum(
int
u){
nodnum[u]
=
1
;
int
i, sz;
sz
=
son[u].size();
for
(i
=
0
; i
<
sz; i
++
)
nodnum[u]
+=
calnum(son[u][i]);
return
nodnum[u];
}
void
dfs(
int
u){
int
i, j, k, sz, v;
sz
=
son[u].size();
for
(i
=
0
; i
<
sz; i
++
)
dfs(son[u][i]);
for
(i
=
0
; i
<
sz; i
++
){
v
=
son[u][i];
for
(j
=
nodnum[u]
-
1
; j
>=
0
; j
--
){
if
(d[u][j]
!=
INF){
for
(k
=
1
; k
<=
nodnum[v]; k
++
){
d[u][j
+
k]
=
min(d[u][j
+
k], d[u][j]
+
d[v][k]);
}
}
}
}
d[u][nodnum[u]]
=
c[u];
}
int
main(){
int
i, j, k, u, v, num, sum, mn;
char
name[
200
];
while
(scanf(
"
%d
"
,
&
n)
!=
0
){
init();
scanf(
"
%d
"
,
&
m);
getchar();
for
(i
=
0
; i
<
n; i
++
){
scanf(
"
%s
"
,
&
name);
u
=
input(name);
scanf(
"
%d
"
,
&
c[u]);
while
(getchar()
==
'
'
){
scanf(
"
%s
"
,
&
name);
v
=
input(name);
son[u].push_back(v);
indeg[v]
++
;
}
}
sum
=
0
;
cnt
=
1
;
d[
0
][
0
]
=
0
;
for
(i
=
1
; i
<=
n; i
++
){
if
(indeg[i]
==
0
){
son[
0
].push_back(i);
sum
+=
c[i];
}
d[i][
0
]
=
0
;
}
c[
0
]
=
sum;
calnum(
0
);
nodnum[
0
]
--
;
dfs(
0
);
int
mn
=
INF;
for
(i
=
m; i
<=
n; i
++
)
if
(d[
0
][i]
<
mn)
mn
=
d[
0
][i];
printf(
"
%d\n
"
, mn);
}
return
0
;
}