zoukankan      html  css  js  c++  java
  • Codeforces 437B The Child and Set

    Description

    描述

    从 $1sim limit$ 中选取若干个互不相同的数字,使得这些数字的 $lowbit$ 和等于 $sum$。

    输入

    两个数 $sum, limit(1leq sum,limitleq 10^5)$。

    输出

    第一行输出 $n$ 表示所选数字的数目,然后第二行 $n$ 个数,表示所选数字。

    如果无解输出$-1$。

    样例

    输入1

    5 5

    输出1

    2
    4 5

    输入2

    4 3

    输出2

    3
    2 3 1

    输入3

    5 1

    输出3

    -1

    解释

    第一个样例中,$lowbit(4)=4,lowbit(5)=1,4+1=5$。
    第二个样例中,$lowbit(1)=1,lowbit(2)=2,lowbit(3)=1,1+2+1=4$。

    Solution

    因为至多选取 $10^5$ 个数字,很自然想到贪心。

    贪心是基于这样一个事实:

    任何一个数字都可以表示为 $sum 2^x$( $x$ 不重复) 的形式

    这是为什么呢,相信大家都知道每个数都有二进制的表示,例如 $1011_{(2)}$ 就显然等于 $1 imes 2^0 + 1 imes 2^1 + 0 imes 2^2 + 1 imes 2^3$,化简一下,也就是 $2^0 + 2^1 + 2^3$。

    换言之,因为二进制的每一位只可能是 $0 / 1$,所以必然可以表示为如上形式。

    然后 $lowbit$ 相信大家也很清楚,表示一个数字的二进制的最后一位 $1$ 的大小,例如 $lowbit(10color{red}1color{black}0_{(2)})= 10_{(2)}$,即 $lowbit(6)=2$,可以用  lowbit(x) = x AND -x  来计算,显然 $lowbit$ 必然是一个$2^{cdots}$(不理解的同学先学习树状数组)。

    我们用一个pair来记录原数和它的 $lowbit$ 值:

    for(int i = 1; i <= lim; i++)
    {
    	S.push_back(make_pair(i & -i, i)); // lowbit and number
    }
    

    然后基于上面那个结论,将pair根据 $lowbit$ 从大到小排序,依次贪心,这里的贪心结论是:如果 $n$ 大于等于当前的 $lowbit$,必然可以选择当前的 $lowbit$ ,然后将 $n$ 减去它,继续贪心,如果最终 $n eq 0$,则无解

    为什么呢?

    $lowbit$ 值显然是有重复的,假设当前在考虑$lowbit_i$,如果一个数可以表示为$sum_{jin[i+1,limit]}lowbit_j$ 的形式,那么这个数加 $lowbit_i$ 必然也可以被表示。

    所以,如果 $n(ngeq lowbit_i)$ 可以被表示,则 $n-lowbit_i$ 也可以被表示。

    sort(S.rbegin(), S.rend()); // sort(big -> small)                     
    for(unsigned i = 0; i < S.size(); i++)          
    {                                               
    	if(n >= S[i].first) // greedy                        
    	{                                           
    		n -= S[i].first;                        
    		ans.push_back(S[i].second);              
    	}                                           
    }                                               
    // output
    if(n)                                           
    {                                               
    	cout << -1 << endl;                         
    }                                               
    else                                            
    {                                               
    	cout << ans.size() << endl;                 
    	for(unsigned i = 0; i < ans.size(); i++)    
    	{                                           
    		cout << ans[i] << ' ';                  
    	}                                           
    }    
    

    于是这题就可以愉快的通过了,完整代码(代码中的 $n$ 表示原题的 $sum $):

    #include <bits/stdc++.h>
    using namespace std;
    int n, lim;
    vector<pair<int, int> > S;
    vector<int> ans;
    int main()
    {
    	cin >> n >> lim;
    	for(int i = 1; i <= lim; i++)
    	{
    		S.push_back(make_pair(i & -i, i));
    	}
    	sort(S.rbegin(), S.rend());
    	for(unsigned i = 0; i < S.size(); i++)
    	{
    		if(n >= S[i].first)
    		{
    			n -= S[i].first;
    			ans.push_back(S[i].second);
    		}
    	}
    	if(n)
    	{
    		cout << -1 << endl;
    	}
    	else
    	{
    		cout << ans.size() << endl;
    		for(unsigned i = 0; i < ans.size(); i++)
    		{
    			cout << ans[i] << ' ';
    		}
    	}
    	return 0;
    }
    

    一句话总结:能减就减,有余无解!

  • 相关阅读:
    代表行为已成为习惯的信号有哪些?
    Java使用JDBC连接Oracle数据库
    JS正则表达式
    java实现内网通信
    纯前端代码实现美团外卖页面
    HTML绘制表格
    教你如何使用谷歌浏览器,亲测可用!
    Java 多线程实现多窗口同时售票简单功能
    实现获取命令行的返回结果
    HTML模仿实现京东登录页面
  • 原文地址:https://www.cnblogs.com/syksykCCC/p/CF437B.html
Copyright © 2011-2022 走看看