UVA 1407 - Caves

版权声明:转载请注明出处 https://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/81606729

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4153

题解

这题做起来没那么直接
有一种大家都熟悉的思想叫做二分答案
那么考虑这样一个问题:如果二分它最后走了 m i d 个节点,那么问题就变成给它那么多能量,看看它能不能走过 m i d 个节点
假设我能构造出一种解让它恰好走过 m i d 个节点,那么构造的方式要么是 d p 要么是贪心,如果贪心,那么我也能构造出更优的解,最终回到了问题原点;所以我只考虑 d p ,f[i][j][0/1]表示从 i 节点开始在子树里面走恰好 j 个点的最小花费,0表示不用回到根节点, 1 表示回到根节点,转移就是树形 d p 套路了,瞎写写就行,别忘了倒着枚举。
我最后只需要看看我走的这个步数需要的最小花费是不是小于等于询问即可。
这个题满足二分性质,所以可以二分,但是实际上根本没有必要,因为 Q 太小了,直接枚举答案即可。

思路总结

这题就难在状态设计
直觉上我应该考虑 f [ i ] [ j ] 表示从 i 出发还剩 j 能量的走的最多的节点数,但是能量太大而节点数很小,因此一个套路就似乎交换状态和表示的东西,由此就想到正解了
至于 [ 0 / 1 ] 这一维,可以手动分析下样例,最后发现我一定会停留在一棵子树中,而且其它的子树都会回到根节点,于是这个就能想到了。

代码

//Ê÷ÐÎDP 
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cmath>
#define maxn 2018
#define int_inf 0x3f3f3f3f
#define cl(x,y) memset(x,y,sizeof(x))
using namespace std;
int f[maxn][maxn][2], head[maxn], tot, to[maxn], w[maxn], nex[maxn], root, fa[maxn], N;
int read(int x=0)
{
    char c, f=1;
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-48;
    return f*x;
}
void adde(int a, int b, int c){to[++tot]=b;w[tot]=c;nex[tot]=head[a];head[a]=tot;}
int min(int a, int b, int c){return min(min(a,b),c);}
int init()
{
    int i, a, b, c;
    tot=0;
    cl(head,0);
    cl(nex,0);
    cl(fa,0);
    cl(f,int_inf);
    N=read();
    if(!N)return false;
    for(i=1;i<N;i++)a=read()+1, b=read()+1, c=read(), fa[a]=b, adde(b,a,c);
    for(root=1;fa[root];root=fa[root]);
    return true;
}
void dp(int pos)
{
    int p, i, j;
    f[pos][1][0]=f[pos][1][1]=0;
    for(p=head[pos];p;p=nex[p])
    {
        dp(to[p]);
        for(i=N;i>1;i--)for(j=1;j<i;j++)
            f[pos][i][0]=min(f[pos][i][0],f[pos][j][1]+w[p]+f[to[p]][i-j][0],w[p]+w[p]+f[to[p]][i-j][1]+f[pos][j][0]),
            f[pos][i][1]=min(f[pos][i][1],f[pos][j][1]+w[p]+w[p]+f[to[p]][i-j][1]);
    }
}
int main()
{
    int Q, x, c=0, i;
    while(init())
    {
        printf("Case %d:\n",++c);
        dp(root);
        Q=read();
        while(Q--)
        {
            x=read();
            for(i=1;f[root][i+1][0]<=x;i++);
            printf("%d\n",i);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/81606729