• P1041 传染病控制(noip2003)(搜索)


    呃呃呃。。。真的是惨烈啊。。。

    今天的模拟赛是真的惨。。。。。

    本题,正解居然是搜索!!!!!!

    蒟蒻自己歪歪了一个貌似是正解但是却连一半都没过的错解。

    先解释一下自己的dp思路把。

     $f[i][u][v][0/1]$表示第i秒(等效于深度)uv之间的边断还是不断。

    转移有两个:

    $f[dep[u]][u][v][1]=size[v];$表示此边断,然后下面的点全对答案有贡献

    然后找到v的儿子

    $f[dep[u]][u][v][0]=max(f[dep[u]][u][v][0],max(f[dep[v]][u][j][0],f[dep[v]][u][j][1]));$

    临收卷前发现貌似第二维可以压掉,也就是:$f[dep[v]][v][0/1]$表示v上面那个边要不要删。

    然后发现随手hack....

    于是,我发现答案统计的时候,会有惊人的重复计算。

    于是,我歪歪了好久,怎么标记统计过了,怎么返回,怎么....

    然后....发现最后好像被我打成了贪心???

    我在每一层去最大值,然后标记,返回,从别的子树里找最大值,然后一直重复到底层。

    于是...我抱着能A掉这题的心情,笑了起来。。。

    然后我rand了好多的的数据,都没有hack掉,我更开心了。

    (先放dp代码:)

    #include<bits/stdc++.h>
    #define g(x) for(int i=head[x];i;i=e[i].next)
    #define l(x) for(int l=head[x];l;i=e[l].next)
    using namespace std;
    const int maxn=505;
    int n,p;
    struct edge
    {
        int to,next;
    }e[maxn];
    int head[maxn],cnt;
    void addedge(int from,int to)
    {
        e[++cnt]=(edge){to,head[from]};
        head[from]=cnt;
    }
    int dep[maxn];
    int size[maxn];
    int fa[maxn];
    void dfs(int u,int f)
    {
        dep[u]=dep[f]+1;
        size[u]=1;
        fa[u]=f;
        g(u)
        {
            int v=e[i].to;
            if(v==f)
            continue;
            dfs(v,u);
            size[u]+=size[v];
        }
    }
    int f[maxn][maxn][2];
    int mp;
    void dp(int u)
    {
        g(u)
        {
            int v=e[i].to;
            if(v==fa[u])
            continue;
            dp(v);
            f[dep[u]][u][v][1]=size[v];
            for(int j=1;j<=n;j++)
            if(fa[j]==v)
            f[dep[u]][v][0]=max(f[dep[u]][v][0],max(f[dep[v]][j][0],f[dep[v]][j][1]));
        }
    }
    int ans;
    int vis[maxn];
    void getans(int u)
    {
        int m=0;
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v==fa[u])
            continue;
            m=max(m,max(f[dep[u]][v][1],f[dep[u]][v][0]));
        }
        for(int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v==fa[u])
            continue;
            if(m==f[dep[u]][v][1]&&vis[u]==0)
            {
                ans+=m;
                vis[u]=1;
                continue;
            }
            else
            {
                getans(v);
            }
        }
    }
             
             
    int main()
    {
        scanf("%d%d",&n,&p);
        for(int i=1;i<=p;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            addedge(x,y);
            addedge(y,x);
        }
        addedge(0,1);
        dfs(1,0);
        dp(1);
        for(int i=1;i<=n;i++)
        mp=max(mp,dep[i]);
        getans(1);
        printf("%d",n-ans);
        return 0;
    }
    View Code

     dp错在于:它会漏状态,也就是说:

    我的dp是从子树开始更新,然鹅,即使我在答案统计上避免了重复,它也从一个dp变成了一个贪心,也就是说,它貌似是等效于每次在一层之内找一个子树最大的点加上,这个贪心是错的。

    从而得出:dp都是错的。

    因为它总会漏状态,如果从儿子更新,同深度的状态没法记录,如果从孙子更新,它的曾孙子没法更新。总而言之就是没法更新完全,也就是有后效性。

    所以,如果想得出正解,就得从最下开始更新子树,而这点dp是没法做到的,所以,能够猜到正解是什么:

    暴力

    怎么暴力怎么写,直接更新,不需要记录状态。

    这个复杂度简直玄学。。。

    如果是分叉很多,这个dfs能被卡成皮皮,但是这题的数据貌似非常水....

    所以,暴力的思路就是:

    记录每个深度的所有点,然后对于这些同深度的点进行更新,找出最大值,一次一次向上更新,一直到根节点。

    感觉这个深搜巨tm像广搜啊....但是....好吧我错了

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 505;
    int n, p;
    vector<int> g[maxn], dep[maxn];
    int vis[maxn];
    int ans, s;
    void dfs(int u, int d)
    {
        dep[d].push_back(u);
        for (int i = 0; i < g[u].size(); i++)
        {
            dfs(g[u][i], d + 1);
        }
    }
    int work(int u, int t)
    {
        int ss = 1;
        vis[u] = t;
        for (int i = 0; i < g[u].size(); i++)
        {
            ss += work(g[u][i], t);
        }
        return ss;
    }
    void getans(int d)
    {
        for (int i = 0; i < dep[d].size(); i++)
        {
            if (vis[dep[d][i]])
                continue;
            s += work(dep[d][i], 1);
            ans = max(s, ans);
            getans(d + 1);
            s -= work(dep[d][i], 0);
        }
    }
    int main()
    {
        scanf("%d%d", &n, &p);
        for (int i = 1; i <= p; i++)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            if (x > y)
                swap(x, y);
            g[x].push_back(y);
        }
        dfs(1, 1);
        getans(2);
        printf("%d", n - ans);
        return 0;
    }
  • 相关阅读:
    设计一个字符串类,并将字符串处理函数的内容进行封装
    C++字符串处理函数【自写】
    文件共享服务器nfs搭建过程
    svn服务器的搭建过程 主要为服务端
    uwsgi和wsgi
    熟悉了下HTTP协议
    ModelForm views.py
    隐藏tomcat nginx版本信息
    csrf
    开发模式
  • 原文地址:https://www.cnblogs.com/ajmddzp/p/11821625.html
Copyright © 2020-2023  润新知