CF440D Berland Federalization
1.本题为树形dp,用f(u,i) 表示以 u 为根,取出 i 个节点子树的最少删边次数。
则有f(u,i)=min( f(u,i-k),f(v,k) ),当k=0时,f(u,i)加一
2.要想输出方案,可以用 g(u,i) 记录一下 f(u,i) 的后继状态,转移时从 g(u,i-k) 复制。后继状态中显然当 i=0 时 u 要被删去。
PS:当根节点不为1时,ans++
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=405;
int n,k;
int id[maxn][maxn];
vector<pair<int,int>> g[maxn][maxn];
struct node{
int v,nxt;
}edge[maxn*2];
int head[maxn],cntE;
void add(int u,int v){
edge[++cntE]=(node){v,head[u]};
head[u]=cntE;
}
int sz[maxn],fa[maxn];
int f[maxn][maxn];
void cpy(int u,int j,int t){
g[u][j].clear();
for(int i=0;i<g[u][t].size();i++){
g[u][j].push_back(g[u][t][i]);
}
}
void init(int u,int p){
sz[u]=1;
fa[u]=p;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==p) continue;
init(v,u);
sz[u]+=sz[v];
}
}
void dfs(int u,int p){
f[u][1]=f[u][sz[u]]=0;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==p) continue;
dfs(v,u);
for(int j=sz[u];j>=0;j--){
for(int k=0;k<=min(sz[v],j);k++){
if(!k){
f[u][j]++;
g[u][j].push_back(make_pair(v,0));
}
else if(f[u][j-k]+f[v][k]<f[u][j]){
f[u][j]=f[u][j-k]+f[v][k];
cpy(u,j,j-k);
g[u][j].push_back(make_pair(v,k));
}
}
}
}
}
void get_ans(int u,int w){
for(int i=0;i<g[u][w].size();i++){
if(g[u][w][i].second==0){
if(g[u][w][i].first!=1) printf("%d ",id[u][g[u][w][i].first]);
}
else get_ans(g[u][w][i].first,g[u][w][i].second);
}
}
int main(){
scanf("%d%d",&n,&k);
memset(f,0x3f,sizeof f);
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
id[u][v]=id[v][u]=i;
add(u,v);
add(v,u);
}
init(1,1);
dfs(1,1);
int ans=f[1][k],rt=1;
for(int u=2;u<=n;u++){
if(k>sz[u]) continue;
if(f[u][k]+1<ans){
ans=f[u][k]+1;
rt=u;
}
}
printf("%d\n",ans);
if(rt!=1){
printf("%d ",id[fa[rt]][rt]);
}
if(k!=sz[rt])
get_ans(rt,k);
}
P4516 潜入行动
1.用状态 f(0/1,0/1,u,i) 表示以 u 为根的子树中有 i 个被选取的点,第一个0/1 表示 u 是否选取,第二个 0/1 表示 u 是否被覆盖。
2.状态转移:
(1) u没有被选取且没有被覆盖:
f(0,0,u.i+j) = ( f(0,0,u,i)*f(0,1,v,j) )
(2) u没有被选取但被覆盖:
f(0,1,u,i+j)=( f(0,1,u,i)*( f(1,1,v,j)+f(0,1,v,j) )+f(0,0,u,i)*f(1,1,v,j) )
(3) u被选取但没有被覆盖:
f(1,0,u,i+j)= ( f(1,0,u,i)*( f(0,1,v,j)*f(0,0,v,j) ) )
(4) u被选取且被覆盖:
f(1,1,u,i+j)=( f(1,1,u,i)*( f(1,1,v,j)+f(0,1,v,j)+f(1,0,v,j)+f(0,0,v,j))+f(1,0,u,i)*( f(1,1,v,j)+f(1,0,v,j) ) )
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10,P=1e9+7;
int n, k;
vector<int> g[N];
int f[2][2][N][101], siz[N], cmp[101][2][2];
void dfs(int u, int fa) {
siz[u] = f[0][0][u][0] = f[1][0][u][1] = 1;
for(int i = 0;i < g[u].size();i ++) {
int j = g[u][i];
if(j == fa) continue;
dfs(j, u);
for(int k1 = 0;k1 <= min(k, siz[u]);k1 ++) {
cmp[k1][0][0] = f[0][0][u][k1];
f[0][0][u][k1] = 0;
cmp[k1][0][1] = f[0][1][u][k1];
f[0][1][u][k1] = 0;
cmp[k1][1][0] = f[1][0][u][k1];
f[1][0][u][k1] = 0;
cmp[k1][1][1] = f[1][1][u][k1];
f[1][1][u][k1] = 0;
}
for(int k1 = 0;k1 <= k && k1 <= siz[u];k1 ++) {
for(int k2 = 0;k2 + k1 <= k && k2 <= siz[j];k2 ++) {
f[0][0][u][k1+k2] = (1ll * f[0][0][u][k1+k2] + 1ll * cmp[k1][0][0] * f[0][1][j][k2]) % P;
f[1][0][u][k1+k2] = ((1ll * f[0][1][j][k2] + 1ll * f[0][0][j][k2]) % P * 1ll * cmp[k1][1][0] % P + 1ll * f[1][0][u][k1+k2]) % P;
f[0][1][u][k1+k2] = (((1ll * f[1][1][j][k2] + f[0][1][j][k2]) % P * 1ll * cmp[k1][0][1]) % P + 1ll * f[0][1][u][k1+k2] + 1ll * cmp[k1][0][0] * f[1][1][j][k2] % P) % P;
f[1][1][u][k1+k2] = (1ll * f[1][1][u][k1+k2] + (1ll * f[1][1][j][k2] + f[0][1][j][k2] + f[1][0][j][k2] + f[0][0][j][k2]) % P * 1ll * cmp[k1][1][1] % P + cmp[k1][1][0] * (1ll * f[1][1][j][k2] + f[1][0][j][k2]) % P) % P;
}
}
siz[u] += siz[j];
}
}
int main() {
cin >> n >> k;
for(int i = 1, a, b;i < n;i ++) {
cin >> a >> b;
g[a].push_back(b), g[b].push_back(a);
}
dfs(1, 0);
cout << (1ll * f[1][1][1][k] + f[0][1][1][k]) % P;
return 0;
}