天天看点

洛谷_1642 规划(分数规划+动态规划)

规划

题目链接:https://www.luogu.com.cn/problem/P1642

题解:

分数规划,设 d [ i ] = a [ i ] − m i d ∗ b [ i ] d[i] = a[i]-mid*b[i] d[i]=a[i]−mid∗b[i].

check:动态规划,求是否存在m个点连通块 d [ i ] d[i] d[i]。 d p [ u ] [ i ] dp[u][i] dp[u][i]代表以 u u u为根节点的子树,选 i i i个节点能得到的最大的 d [ i ] d[i] d[i]和。

递推方程 d p [ u ] [ j + k ] = m a x ( d p [ u ] [ j + k ] , d p [ u ] [ j ] + d p [ v ] [ k ] ) ( 1 ≤ j ≤ s z [ u ] , 0 ≤ k ≤ s z [ v ] ) dp[u][j+k] = max(dp[u][j+k], dp[u][j]+dp[v][k])(1\le j \le sz[u],0\le k \le sz[v]) dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k])(1≤j≤sz[u],0≤k≤sz[v])。

#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-6
   
using namespace std;
typedef long long LL;   
typedef pair<int, int> P;
const int maxn = 120;
const int mod = 10000;
int a[maxn], b[maxn], sz[maxn];
double d[maxn], dp[maxn][maxn], tmp[maxn];
vector<int> g[maxn];
bool check(int n, int m);
void dfs(int u, int f, int m);

int main()
{
    int n, m, i, j, k;
    double l, r;
    scanf("%d %d", &n, &m);
    m = n-m;
    for(i=1;i<=n;i++)
        scanf("%d", &a[i]);
    for(i=1;i<=n;i++)
        scanf("%d", &b[i]);
    for(i=1;i<n;i++){
        scanf("%d %d", &j, &k);
        g[j].push_back(k);
        g[k].push_back(j);
    }
    l = 0, r = 1e12;
    while(l+eps<r)
    {
        double mid = (l+r)/2;
        for(i=1;i<=n;i++)
            d[i] = a[i] - mid*b[i];
        if(check(n, m))l = mid;
        else r = mid;
    }
    printf("%.1f\n", l);
    return 0;
}

bool check(int n, int m)
{
    for(int i=0;i<=n;i++)
        for(int j=0;j<=m;j++)
            dp[i][j] = -1e8;
    dfs(1, 0, m);
    for(int i=1;i<=n;i++)
        if(dp[i][m] >= 0.0)return true;
    return false;
}

void dfs(int u, int f, int m)
{
    sz[u] = 1;
    dp[u][0] = 0;
    dp[u][1] = d[u];
    for(int i=0;i<g[u].size();i++){
        int v = g[u][i];
        if(v == f)continue;
        dfs(v, u, m);
        for(int j=0;j<=m;j++)
            tmp[j] = dp[u][j];
        //因为当前的根节点必选,所以j从1开始
        for(int j=1;j<=sz[u];j++)
            for(int k=0;k<=sz[v];k++)
                if(j+k<=m)
                    tmp[j+k] = max(tmp[j+k], dp[u][j]+dp[v][k]);
        for(int j=0;j<=m;j++)dp[u][j] = tmp[j];
        sz[u] += sz[v];
    }
}