BZOJ1065奥运物流 NOI2008

这个题一开始没有明确的写出每个点的贡献 , 暴力+贪心 , 本来只能得50分 , 但由于数据很弱 , 所以可以拿70分。 CCF啊……

提示:
1. 这个题每个点的贡献能不能简化成一个明确一点的式子呢?
2. n个点 , 然而 n条边 , 要是少一条边就好了……

代码后有较为详细的说明:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <deque>
#include <stack>
#include <algorithm>
#include <queue>

using namespace std;
const int maxn = 70;
const double INF = 1e20;

vector<int> g[maxn];
int vis[maxn][maxn][maxn];
double d[maxn][maxn][maxn];

int n , m;
double k;
int Next[maxn];
double c[maxn];

void init()
{ 
    for(int i=1;i<=n;i++) g[i].clear();
    for(int i=1;i<=n;i++) 
        for(int j=0;j<=m;j++) for(int k=0;k<=i;k++) vis[i][j][k] = 0 ,  d[i][j][k] = -INF; 
}

double f[maxn][maxn];

void proDp(int u , int dep)
{
    for(int j=0;j<=g[u].size();j++) for(int k=0;k<=m;k++) f[j][k] = -INF; f[0][0] = 0;

    for(int j=0;j<g[u].size();j++)
    {
        int to = g[u][j];
        for(int k=0;k<=m;k++) for(int l=0;l<=k;l++) 
            f[j+1][k] = max(f[j+1][k] , f[j][l]+d[to][k-l][dep]);
    }
}

void dp(int u)
{
    for(int i=0;i<g[u].size();i++) dp(g[u][i]);

    proDp(u, 2);
    for(int i=0;i<=n;i++) for(int j=1;j<=m;j++) d[u][j][i] = f[g[u].size()][j-1]+c[u]*k;

    for(int i=0;i<=n;i++)
    {
        proDp(u, i+1);
        for(int j=0;j<=m;j++) d[u][j][i] = max(d[u][j][i] , f[g[u].size()][j]+c[u]*pow(k, i));
    }
}

int main(int argc, char *argv[]) {

    scanf("%d%d%lf",&n,&m,&k);
    for(int i=1;i<=n;i++) scanf("%d " , &Next[i]);
    for(int i=1;i<=n;i++) scanf("%lf" , &c[i]);

    double res = -INF;
    for(int i=Next[1] , len = 2;i!=1;i = Next[i] , len++)
    {
        init();
        for(int j=2;j<=n;j++) if(j!=i) g[Next[j]].push_back(j); else g[1].push_back(j);
        dp(1);
        res = max(res , d[1][m-(Next[i]!=1)][0]/(1-pow(k, len)));
    }

    printf("%.2lf\n" , res);
    return 0;
}

树形DP , 08年的题也是爱上这玩意了

把方程列出来然后用c1 , c2 ….来表示R1 , 可以发现每个点的贡献与这个点到1号结点的距离有关。整个式子又与1到自己的距离有关 , 等等 , 还记得n个点n条边的图吗。 这不就是一棵树多一条边? 所以 , 对于这种带环树 , 最常用的技巧之一 , 把环拆掉。

两次dp , 分别是树形DP和对于单棵树的儿子们的背包DP , 这都不难理解 , 这份代码一个优秀的写法, 就是把背包DP抽出来 , 逻辑很明了。

你可能感兴趣的:(dp,bzoj)