zoukankan      html  css  js  c++  java
  • P1036 选数(DFS)

    P1036 选数

    题目描述

    已知 n个整数 x1,x2,…,xn,以及11个整数k(k<n)。从n个整数中任选k个整数相加,可分别得到一系列的和。例如当n=4,k=3,4个整数分别为3,7,12,19时,可得全部的组合与它们的和为:

    3+7+12=22

    3+7+19=29

    7+12+19=38

    3+12+19=34。

    现在,要求你计算出和为素数共有多少种。

    例如上例,只有一种的和为素数:3+7+19=29。

    输入格式

    键盘输入,格式为:

    n,k1≤n≤20,k<n)

    x1,x2,…,x**n(1≤xi≤5000000)

    输出格式

    屏幕输出,格式为: 1个整数(满足条件的种数)。

    输入输出样例

    输入

    4 3
    3 7 12 19
    

    输出

    1
    

    题解

    因为n<=20,所以可以放心地DFS。因为要遍历每个数的选与不选,所以共有O(2^n^)的复杂度2^20^(1m)约为1e6,能过。

    又因为sum最多是20*5e6=1e8,可以不用ll(我比较谨慎,所以开了ll)

    真正要判断是否为质数的情况就是C(n,k),最差的时候n为20,k为10,方案数约有1.847e5种。

    Judge函数为O(根号sum),sum最大为1e8,这时复杂度为1e4,如果执行1.847e5次就是1.847e9的复杂度。

    综上所述,判断质数的总复杂度才是程序的主要复杂度。

    这样写,虽然有些危险,不过这只是最差的情况,除非某sum真的是质数,不然到不了O(根号sum)的情况。

    可以先预处理出1e4以内的所有质数,可以在这里得到优化。(但是貌似此题没有必要这样)

    而存答案的变量ans无需用ll,因为总情况约共有1.847e5种,而每种情况自然不都是符合条件的。

    DFS的边界有两个:

    一:已经选够了k个数,直接判断sum,如果是质数,就返回一;不是,就返回0;

    二:决策完所有n个数,但是没有选够k个数,直接跳出(可以优化,在决策完n-i个数后,只要选中的数比k-i少,就算以后每个数都选也不可能够k个,直接跳出)我没用优化,貌似也能过。

    决策第i个数时,分为两类:选i和不选i,两种情况的方案数之和,就是这个状态的总方案数。

    DFS式:

    [DFS_{Left,Sum,At}=DFS_{Left-1,Sum+a_{At+1},At+1}+DFS_{Le,Sum,At+1} ]

    关于状压DP

    这道题也可以用状压来做,就是用下标二进制数的第i位的1或0来表示第i个数选或不选。

    但是考虑到这道题每个状态只访问一次而且空间复杂度为4MB,所以不光时间复杂度没优化,反而反向优化空间复杂度。

    结论:状压DP是大佬炫技(xian de dan teng)的花式解法,不推荐。

    代码~亲测AC~:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    int n, k, a[23];
    bool J(int un)//Judge函数,判断un是否是质数
    {
    	int s = sqrt(un);
    	for (int ii = 2; ii <= s; ii++)
    	{
    		if (un % ii == 0) return false;
    	} return true;
    }
    int DFS(int Le, long long sum, int At)//返回以(剩余Le个数字时已选数字总和为sum并且当前刚好决策完At是否选)的状态为基础,接下来所有方案中(最后选中的数和为质数的可行方案数)
    {
    	if (At > n) {//如果已经到了n+1还没返回,说明选数不足k
    		return 0;//这个方案不可行
    	}
    	if (Le <= 0)//选够k个数
    		return J(sum);//判断质数
    	int ans = 0;
    	ans += DFS(Le - 1, sum + a[At+1], At + 1);//选择a[i]的方案数
    	ans += DFS(Le, sum, At + 1);//不选的方案数
    	return ans;
    }
    int main()
    {
    	cin >> n >> k;
    	for (int i = 1; i <= n; i++)
    	{
    		cin >> a[i];
    	}
    	cout << DFS(k, 0, 0);//一开始一个数没选(还差k个数选完),当前总和为0,已经决策0个数
    	return 0;
    }
    
  • 相关阅读:
    git merge远程合并
    开发中必知必会的常用Linux命令
    mysql双机双向热备
    入门级实操教程!从概念到部署,全方位了解K8S Ingress!
    linux常用命令,你应该了解的Linux知识
    MFC的静态库.lib、动态库.dll(包含引入库.lib)以及Unicode库示例
    Java 表达式之谜:为什么 index 增加了两次?
    Vavr Option:Java Optional 的另一个选项
    一文详解 Java 的八大基本类型!
    如何找到真正的 public 方法
  • 原文地址:https://www.cnblogs.com/Wild-Donkey/p/12292280.html
Copyright © 2011-2022 走看看