HDU 5890 Eighty seven(dp+bitset)

本文介绍了一种使用动态规划解决特定数值组合问题的方法。给定一系列整数,在删除三个数后,判断是否能从剩余的数中选出十个数,使它们的总和等于87。文章详细解释了通过枚举和状态转移方程来解决问题的过程,并提供了实现代码。

Description

给出nn个数a1,...,an,每次查询删去三个数(可能重复),问是否可能从剩下的数中取十个数使其和为8787

Input

第一行一整数TT表示用例组数,每组用例首先输入一整数n,之后输入nn个整数a1,...,an,之后输入一整数qq表示查询数,每组查询输入三个整数i,j,k表示删去ai,aj,akai,aj,ak(1T5,1n50,1q105)(1≤T≤5,1≤n≤50,1≤q≤105)

Output

对于每组查询,如果删去ai,aj,akai,aj,ak后可以从剩余数中拿十个使得其和为8787则输出YesYes,否则输出NoNo

Sample Input

1
12
1 2 3 4 5 6 7 8 9 42 21 22
10
1 2 3
3 4 5
2 3 2
10 10 10
10 11 11
10 1 1
1 2 10
1 11 12
1 10 10
11 11 12

Sample Output

No
No
No
Yes
No
Yes
No
No
Yes
Yes

Solution

枚举删去的三个数ai,aj,akai,aj,ak,以dp[r][s][t]dp[r][s][t]表示从前rr个数中取s个数和为tt的方案是否存在,如果r=i/j/k则第rr个数不选,否则根据第r个数是否选有转移dp[r][s][t]|=dp[r1][s1][tar]|dp[r1][s][t]dp[r][s][t]|=dp[r−1][s−1][t−ar]|dp[r−1][s][t],时间复杂度O(880n4)O(880n4),用bitsetbitset优化一下第三维即可,预处理出所有情况之后对于查询O(1)O(1)输出即可

Code

#include<cstdio>
#include<algorithm>
#include<bitset>
using namespace std;
bitset<88>dp[11];
int T,n,q,a[55],mark[55][55][55];
int check(int i,int j,int k)
{
    for(int i=0;i<=10;i++)dp[i].reset();
    dp[0][0]=1;
    for(int s=1;s<=n;s++)
        if(s!=i&&s!=j&&s!=k)
            for(int t=10;t>=1;t--)
                dp[t]|=dp[t-1]<<a[s];
    if(dp[10][87])return 1;
    return 0;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            for(int j=i;j<=n;j++)
                for(int k=j;k<=n;k++)
                    mark[i][j][k]=check(i,j,k);
        scanf("%d",&q);
        while(q--)
        {
            int b[3];
            scanf("%d%d%d",&b[0],&b[1],&b[2]);
            sort(b,b+3);
            printf("%s\n",mark[b[0]][b[1]][b[2]]?"Yes":"No");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值