E.pXIphp

【树形DP】MZOJ_1063_士兵守卫

本题也是这三天来在下写的几篇树形DP之一,但是不知道为什么洛谷上面老是unknown error,。。。直接去了UVa,说我编译错误。。。我在想是不是头文件的原因,于是被逼无奈,交了一道c89的代码。(结果后来还是CE了)

我真他娘是B了狗了。。。

所以,因为测试数据只过了我们学校的OJ(需要注册才可看题),我会担心有可能代码有问题,欢迎大家杀我祭天指正错误。


题目描述[传送门]

Bob特别喜欢战略游戏,但有时他不能尽快找到最优解,所以他就很伤心。现在他又有一个问题,他必须保卫一个中世纪的城市,这个城市的道路形成了一棵树。他需要在树的节点上放最少的士兵来观察所有的边。你能帮助他么?

例如下图就只需要一个士兵放在1号节点。

 

输入

输入文件soldier.in中有多组数据,每组数据的第一行N表示点的个数。接下来N行每行格式如下

  x:(k) a1 a2 … ak(x为点的编号,k为与其相连的子节点个数,a1, a2, …, ak分别为子节点的编号)

输出

输出文件soldier.out,对于每组数据输出一行一个数,即最少士兵数。

样例输入

4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)

样例输出

1
2

提示

0 < N<= 1500, 0
<= x < N

 


 

于是这是我写的第三篇关于树形DP的博客。我越来越熟悉这个美妙而不失简洁的算法。。。。然而选课你还是做不来hiahiahia

让我们来简化分析一下:

给定一个无向多叉树,一个节点可以影响和它直接连接的节点,那请问需要至少多少个节点才可以使所有节点都被影响?

动规最重要是定义状态,一个好的状态可以让你的程序写起来更加容易。

在这里我们规定:f[u][0/1]用于表示u号节点放士兵或者不放士兵时的最小士兵数。

那这个时候我们就要想状态转移方程了

要是u节点不放士兵,那必须至少有一条与之相邻的点放了士兵,要是放士兵,则可放可不放,很容易得出f[u][0]=f[u][0]+f[v][1];//v为一个与u相邻的点   f[u][1]=min(f[v][1],f[v][0])

并且在最后不要忘了把f[u][1]给++。

这样状态转移和定义出来了,那么答案就是min(f[root][0],f[root][1]);

下面给出完整代码:

#include<bits/stdc++.h>
namespace Jason{
    inline void scan(int &x){
    int f=1;x=0;char s=getchar();
    while(s<'0' || s>'9'){if(s=='-') f=-1;s=getchar();}
    while(s>='0' && s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
    inline void print(int x){
        if(x<0){putchar('-');x=-x;}
        if(x>9)print(x/10);char s=x%10+'0';
        putchar(s);
    }
struct Edge_B{
    int to,dis;
    Edge_B* nxt;
Edge_B(int to=-1,int dis=-1,Edge_B* n=NULL){this->to=to,this->dis=dis,this->nxt=n;} 
};
}
using namespace std;
using namespace Jason;
const int maxn=15000+5;
//--------------------
int n,m,cnt=0;
struct Edge{
    int to,nxt;
}edge[maxn<<1];int head[maxn];
int f[maxn][2];
//--------------------
void add(int x,int y)
{
    edge[++cnt].nxt=head[x];
    edge[cnt].to=y;
    head[x]=cnt;
}

void dp(int u,int fa)
{
    for(int i=head[u];i!=-1;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        dp(v,u);
        f[u][0]+=f[v][1];
        f[u][1]+=min(f[v][0],f[v][1]);
    }
    f[u][1]++;
}

int main()
{
    //freopen("in","r",stdin);
    int a,b,c;
    while(scanf("%d",&n)==1)
    {
        memset(head,-1,sizeof(head));
        memset(f,0,sizeof(f));
        for(int i=0;i<n;++i)
        {
            scanf("%d:(%d)",&a,&b);
        for(int i=0;i<b;++i) scanf("%d",&c),add(a,c),add(c,a);
        }
        dp(0,-1);
        print(min(f[0][1],f[0][0]));putchar('\n');
    }
    return 0;
}

 

 然而我还是不会选课。。。。。

 

posted @ 2019-01-22 11:41  Ekretn  阅读(280)  评论(2编辑  收藏  举报