5788. 【NOIP提高A组模拟2018.8.9】餐馆

题目大意

给定一棵树,树有权值。
有两个操作:
1.走一条边
2.获得一个点的权值(权值获得后清零)
两个操作均花费1单位时间
固定从1号点出发,在m个单位时间内最多能获得多少贡献?

数据范围

对于10%的数据,N≤20。
对于50%的数据,N≤110。
对于100%的数据1 ≤ N, M ≤ 500,1 ≤ A[i]≤ 10^6。
第5到第10个测试点都有多个子测试。

Solution

10%:爆搜
时间复杂度:O(2n)
100%:直接DP,设f[i][j][0..1]
f[i][j][0]表示以 i 为根的子树中,花费 j 单位时间,最
终回到 i 的最大收益。
f[i][j][1]表示以 i 为根的子树中,花费 j 单位时间,最
终不必回到 i 的最大收益。
转移显然,注意细节处理
时间复杂度:O(n2)

Code

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 5e2 + 5;

struct Edge
{
    int to,next;
}edge[N << 1];

int n,m,tot,x,y,ans;
int a[N],st[N],f[N][N][2],vis[N];

void link(int u,int v)
{
    edge[++tot].to = v;
    edge[tot].next = st[u];
    st[u] = tot;
}

void dfs(int x)
{
    vis[x] = 1;
    for (int l = st[x] ; l ; l = edge[l].next)
    {
        int v = edge[l].to;
        if (!vis[v])
        {
            dfs(v);
            for (int j = m ; j >= 0 ; j--)
                for (int k = m - j ; k >= 0 ; k--)
                {
                    if (j + k + 1 <= m) f[x][j + k + 1][1] = max(f[x][j + k + 1][1],f[x][j][0] + f[v][k][1]);
                    if (j + k + 2 <= m) f[x][j + k + 2][0] = max(f[x][j + k + 2][0],f[x][j][0] + f[v][k][0]);
                    if (j + k + 2 <= m) f[x][j + k + 2][1] = max(f[x][j + k + 2][1],f[x][j][1] + f[v][k][0]);
                }
        }
    }
    for (int j = m ; j >= 1 ; j--)
        f[x][j][0] = max(f[x][j][0],f[x][j - 1][0] + a[x]),f[x][j][1] = max(f[x][j][1],f[x][j - 1][1] + a[x]);
}

int main()
{
    freopen("dostavljac.in","r",stdin);
    freopen("dostavljac.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]);
    for (int i = 1 ; i < n ; i++) scanf("%d%d",&x,&y),link(x,y),link(y,x);
    dfs(1);
    printf("%d\n",max(f[1][m][0],f[1][m][1]));
    fclose(stdin);
    fclose(stdout);
    return 0;
}
阅读更多

没有更多推荐了,返回首页