【XSY2444】【BZOJ4042】【CERC2014】【luogu4757】Parades(树形dp+状压dp)

21 篇文章 0 订阅
9 篇文章 0 订阅

题面

Description

从前有个A国,它有 n n n个城市和 n − 1 n-1 n1条道路。每条路连接两个城市。城市之间两两可达。每个城市与不超过10条道路相连。

现在给出 m m m条路径,要求从这些路径中选出尽量多的路径,使得它们不两两相交。(这里的相交指经过同一条边,经过同一个点不算相交)

Input

输入的第一行为测试数据组数 T T T

对于每组测试数据:

第一行为一个整数 n ( 2 ≤ n ≤ 1000 ) n(2≤n≤1000) n(2n1000) ,表示城市的数量。

接下来 n − 1 n−1 n1 行,每行两个整数 a , b ( 1 ≤ a ≠ b ≤ n ) a,b(1≤a≠b≤n) a,b(1a=bn) ,表示城市 a a a b b b 有一条道路。每个城市最多与10条道路相连。

接下来一个整数 m ( 0 ≤ m ≤ n ( n − 1 ) 2 ) m(0≤m≤\frac{n(n−1)}{2}) m(0m2n(n1))

接下来 m m m 行,每行2个整数 u i u_i ui, v i ( 1 ≤ u i ≠ v i ≤ n ) v_i(1≤ui≠vi≤n) vi(1ui=vin) ,表示CJB规划的 m m m 条路线。

Output

对于每组测试数据,输出一个整数表示路线的最大数量。

Sample Input

1
6
1 2
2 3
3 4
3 5
3 6
4
1 3
4 5
5 6
6 4

Sample Output

2

HINT

样例解释

选择 ( 1 , 3 ) (1,3) (1,3), ( 4 , 5 ) (4,5) (4,5)

数据范围

∑ n \sum{n} n 为全部 T T T 组测试数据中 n n n 的总和。

对于 10 % 10\% 10%的数据, 2 ≤ n ≤ 50 2≤n≤50 2n50, 1 ≤ m ≤ 20 1≤m≤20 1m20, T ≤ 4 T≤4 T4

对于 40 % 40\% 40%的数据, 2 ≤ n ≤ 50 2≤n≤50 2n50, ∑ n ≤ 3600 \sum{n}≤3600 n3600, T ≤ 100 T≤100 T100

对于所有数据, 2 ≤ n ≤ 1000 2≤n≤1000 2n1000, ∑ n ≤ 18000 \sum{n}≤18000 n18000, T ≤ 100 T≤100 T100

题解

考虑树形 d p dp dp,设 d p [ i ] dp[i] dp[i]表示以 i i i为根的子树中路线的最大数量(即这棵子树的最优解), u n [ i ] [ ] un[i][] un[i][]表示以 i i i为根的子树中,所有的点 u u u,当且仅当 i i i u u u的路径上不与 i i i子树的最优解中的某条路径相交。

那么考虑用子树合并根。

对于 d p [ u ] dp[u] dp[u],我们先加上每个儿子的 d p dp dp值,然后我们再考虑下面这两种情况:

  1. 对于 u u u的某一个儿子 s o n son son,如果 s o n son son的子树内(包括 s o n son son自己)有一个点 v = u n [ s o n ] [ i ] v=un[son][i] v=un[son][i] u u u之间有路径 ( u , v ) (u,v) (u,v),如图:

    在这里插入图片描述
    可能对于某一个儿子 s o n son son,这种路径可能有很多条,但是选任意一条都可以,因为边 ( u , v ) (u,v) (u,v)只能走一遍。

  2. 对于 u u u的某两个不同的儿子 s o n 1 son_1 son1 s o n 2 son_2 son2,它们各自子树内(包括 s o n 1 son_1 son1 s o n 2 son_2 son2)分别有两个点 a = u n [ s o n 1 ] [ i ] a=un[son_1][i] a=un[son1][i] b = u n [ s o n 2 ] [ j ] b=un[son_2][j] b=un[son2][j],且有一条路径 ( a , b ) (a,b) (a,b),我们就把 l i n k [ s o n 1 ] [ s o n 2 ] link[son_1][son_2] link[son1][son2]设为 1 1 1,如图:

    在这里插入图片描述
    那么对于某个儿子 s o n 1 son_1 son1,它可能可以匹配到很多个 s o n 2 son_2 son2,所以怎么选择才方案最优是解决问题的关键,所以我们考虑状压 d p dp dp

    f [ i ] f[i] f[i]表示状态 i i i的最优解,状态 i i i的二进制表达式的第 x x x位若为 1 1 1,则表示已经考虑过加入这个儿子的情况了。

    那么我们从小到大枚举 i i i,找到 a = _ _ b u i l t i n _ c t z ( i ) a=\_\_builtin\_ctz(i) a=__builtin_ctz(i) l b = l o w b i t ( i ) lb=lowbit(i) lb=lowbit(i),则 i i i可以从 i − l b i-lb ilb转移过来,因为在 i − l b i-lb ilb的二进制表达式中,第 a a a位为 0 0 0;在 i i i的二进制表达式中,第 a a a位为 1 1 1

    所以现在我们考虑的是考虑过加入儿子 a a a,也就是上文的 s o n 1 son_1 son1,那么我们还要枚举 s o n 2 son_2 son2,即下文的 b b b

    对于每一个 b b b,当 l i n k ( a , b ) = 1 link(a,b)=1 link(a,b)=1 i i i的二进制表达式中第 b b b位为 1 1 1时,我们取:

    f [ i ] = m a x ( f [ i ] , f [ i − ( 1 < < a ) − ( 1 < < b ) ] + 1 ) f[i]=max(f[i],f[i-(1<<a)-(1<<b)]+1) f[i]=max(f[i],f[i(1<<a)(1<<b)]+1)

    即从 i i i的第 a a a位为 0 0 0且第 b b b位为 0 0 0时转移过来。

    最后 d p [ u ] + = f [ 2 u 的儿子个数 − 1 ] dp[u]+=f[2^{u\text{的儿子个数}}-1] dp[u]+=f[2u的儿子个数1]即可。

这样就合并完成了。

记得合并完后维护一下 u n [ u ] un[u] un[u]

最后输出 d p [ 1 ] dp[1] dp[1]就好了。

完整代码加注释如下:

#include<bits/stdc++.h>
 
#define N 1010
#define D 15
 
using namespace std;
 
int T,n,m;
int dp[N],f[1<<D];
int cnt,head[N],nxt[N<<1],to[N<<1];
bool vis[N][N],link[D][D];
 
vector<int>un[N];
 
int lowbit(int x)
{
    return x&-x;
}
 
void init()
{
    cnt=0;
    memset(head,0,sizeof(head));
    memset(vis,false,sizeof(vis));
    memset(dp,0,sizeof(dp));
}
 
void adde(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
 
void dfs(int u,int fa)
{
    int son[D],tot=0;
    for(int i=head[u];i;i=nxt[i])
    {
        if(to[i]!=fa)
        {
            dfs(to[i],u);
            dp[u]+=dp[to[i]];
            son[tot++]=to[i];   //存储每一个儿子
        }
    }
    for(int i=0;i<tot;i++)
    {
        for(int j=0;j<un[son[i]].size();j++)
        {
            if(vis[un[son[i]][j]][u])   
            {
                dp[u]++;
                un[son[i]].clear();//由于已经选了一条边了,所以为了下面的维护un[u],我们把un[son[i]]清零
            }       
        }
    }
    memset(link,false,sizeof(link));
    for(int i=0;i<tot;i++)
    {
        for(int j=i+1;j<tot;j++)
        {
            int a=son[i],b=son[j];
            for(int p=0;p<un[a].size();p++)
            {
                for(int q=0;q<un[b].size();q++)
                {
                    if(vis[un[a][p]][un[b][q]])
                    {
                        link[i][j]=link[j][i]=true;//标记son[i]与son[j]各自子树之间有路径
                        break;
                    }
                }
                if(link[i][j])
                    break;
            }
        }   
    }
    f[0]=0;
    int maxn=(1<<tot)-1;
    for(int i=1;i<=maxn;i++)
    {
        f[i]=f[i-lowbit(i)];
        int a=__builtin_ctz(i);
        for(int b=a+1;b<tot;b++)//枚举son2
            if(link[a][b]&&((i>>b)&1))
                f[i]=max(f[i],f[i-(1<<a)-(1<<b)]+1);
    }
    dp[u]+=f[maxn];
    un[u].push_back(u);
    for(int i=0;i<tot;i++)
        if(f[maxn]==f[maxn-(1<<i)])
            for(int j=0;j<un[son[i]].size();j++)
                un[u].push_back(un[son[i]][j]);//维护un[u]
}
 
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            un[i].clear();
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            adde(u,v),adde(v,u);
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            vis[u][v]=vis[v][u]=true;
        }
        dfs(1,0);
        printf("%d\n",dp[1]);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,你遇到的问题是在发送HTTP POST请求时收到了403 Forbidden的错误。这个错误通常表示你没有权限访问所请求的资源。 要解决这个问题,你可以采取以下步骤: 1. 首先,确保你的请求URL正确,并且你有权限访问该URL。你可以尝试在浏览器中直接访问该URL,看看是否能够成功访问。 2. 如果你确定URL是正确的,并且你有权限访问,那么可能是你的请求中缺少了必要的身份验证信息。你可以检查你的请求头中是否包含了正确的身份验证信息,比如Token或用户名密码。 3. 另外,你还可以检查服务器端的配置,确保你的请求被正确地处理和授权。你可以查看服务器的日志,以了解更多关于403错误的详细信息。 综上所述,当你收到403 Forbidden错误时,你应该首先检查URL和权限,然后确保请求中包含了正确的身份验证信息。如果问题仍然存在,你可以进一步检查服务器端的配置和日志,以找出问题的根本原因。 #### 引用[.reference_title] - *1* [kubeadm init报错10248...(The HTTP call equal to ‘curl -sSL http://localhost:10248/healthz‘ failed)](https://blog.csdn.net/weixin_45969972/article/details/123529966)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c/c++使用libcurl库做http客户端及封装(HTTP_GET和HTTP_POST)](https://blog.csdn.net/xsy29000/article/details/103181267)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值