zoukankan      html  css  js  c++  java
  • 【u210】kfc

    Time Limit: 1 second
    Memory Limit: 128 MB

    【问题描述】

    最近Kfc新开了个KFC,该KFC提供N种食物,分别用1-N给这些食物编号,食物的价格与其编号有关,满足第K种食物的价格为2^(K-1),例如
    : 食物的编号 1 2 3 4 5 6 7 8 9 10……
    价格 1 2 4 8 16 32 64 128 256 512 ……
    每位顾客最多可以选择L种食物,且每种食物仅一份。
    当顾客选择食物时,他会说:我要第M便宜的食物组合。
    Kfc的工作就是计算第该食物组合的价格。
    一样食物都不要也是一种组合,因此第1便宜的食物组合价格为0。


    【输入格式】

    一行,包含三个整数N (1<=N<=30),,L (1<=L<=N),M,用一个空格隔开。数据保证存在第M便宜的食品组合。

    【输出格式】

    一行,包含一个整数P,P为第M便宜的食品组合的价格。

    【数据规模】

    Sample Input1

    5 3 19
    
    
    

    Sample Output1

    19

    【题解】

    先宣泄一下:这题真TM****.
    然后是思路。
    很明显。2^(i-1)。和二进制有关。
    然后。如果你一开始一个一个去累加。要加很多次。然后会超时5个点左右。(有分!我宁愿只要50分)
    所以我们用排列组合去做。
    比如我们想知道最高位的1要放在哪里。
    比如放在i;
    sum[i] += C[i-1][0..l-1]
    这边的组合数就表示在前i-1个0当中选出0..l-1(因为第i位放了一个1所以只剩l-1个)个1
    然后sum[i] = sum[i] + sum[i-1];
    我们则要找到一个i
    满足sum[i] <= m <sum[i+1];(如果sum[i] == m则只要在i-l+1..i这些位置上置1就可以了。然后直接输出答案不用继续搜索)
    然后若是m>sum[i],则记录这个i停止break掉i的循环。
    然后a[i+1]=1,表示在i+1这个位置放一个1;
    然后对于00...001(这里的1是第i+1)位。
    我们要在1的前面再加上几个1.以达到修改后的数字字典序是第m-sum[i]的目的;
    可以发现这是一个递归的过程
    直接执行solve(i,l-1,m-sum[i]);
    表示在1..i中找到一个合适的最高位,可以放的1的数目为l-1(因为已经放了一个1所以可选食品减少了1);
    然后变成求倒数第m-sum[i]便宜的物品组合了。

    【代码】

    #include <cstdio>
    
    int n, l, m, a[35] = { 0 }, two_n[32];
    int c[35][35] = { 0 };
    int sum[31] = { 0 };
    bool done = false;
    
    int min(int a, int b) //返回a和b中的较小值。
    {
    	return a > b ? b : a;
    }
    
    void input_data() //输入数据
    {
    	scanf("%d%d%d", &n, &l, &m);
    }
    
    void init() //用杨辉三角和组合数对应的关系递推出组合数
    {
    	for (int i = 0; i <= 30; i++)
    		c[i][0] = 1, c[i][i] = 1;
    	for (int i = 1; i <= 30; i++)
    		for (int j = 1; j <= i; j++)
    			c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
    }
    
    void output_ans() //输出答案
    {
    	two_n[0] = 1;
    	for (int i = 1; i <= 31; i++)
    		two_n[i] = two_n[i - 1] * 2; //预处理出2^n
    	int sum = 0;
    	for (int i = 1; i <= 30; i++) //按照2进制转10进制的方法获取答案
    		sum += (two_n[i - 1] * a[i]);
    	printf("%d
    ", sum);
    }
    
    void solve(int nn, int ll, int mm) //在1..nn中找一个合适的最高位放下一个1,然后可以选的食品数目为ll
    {//要找的是第mm便宜的。。
    	int n = nn, l = ll, m = mm, pos1 = 0;
    	sum[0] = 1; //要注意什么都不选也是一种组合。
    	for (int i = 1; i <= n; i++) //最高位在第i位
    	{ //接下来枚举前i-1个空位放几个1(在i-1和l-1之间求一个最小值作为上限)
    		sum[i] = 0;
    		for (int j = 0; j <= min(i - 1, l - 1); j++)
    			sum[i] += c[i - 1][j];
    		sum[i] += sum[i - 1]; //表示1放在i会有多少种组合。
    	}
    	for (int i = 0; i <= n; i++)
    		if (sum[i] == m) //如果恰好为所需要的 
    		{
    			int begin = i - l + 1; //00..01(1在第i位)。在i-l+1到i这段区间内。全部赋值为1.
    			if (begin < 1) //这是在前i个位置放置L个1的所有方案中最贵的。也就是我们所需要的。
    				begin = 1; //如果小于1了 (注意是有l种食品可以选 但没说全要选!);
    			for (int j = begin; j <= i; j++) 
    				a[j] = 1;
    			m -= sum[i]; //直接减掉。当然不减也没事
    			done = true;//这个东西没用。
    			return;//直接退出递归。然后就会输出答案了。
    		}
    		else if (sum[i] < m && sum[i + 1] >m) //如果找到一个放1的合适位置
    		{
    			pos1 = i + 1; //记录放1的位置
    			m -= sum[i]; //减去前i个位置放L个1的方案数。接下来在第i+1个位置放一个1
    			break; //然后递归求前i个位置放L-1个物品的方案数,所求字典序变成m-sum[i]。
    		}
    	if (pos1 != 0) //把1放置 其实是一定能够找到这样一个pos1的。
    		a[pos1] = 1;
    	if (l == 1) //这是递归的边界。我不会跟你讲这个是可以去掉的。
    		return;
    	if (pos1 == 1)
    		return;
    	solve(pos1 - 1, l - 1, m);//递归求前i个位置放置l-1个1,然后m已经减去sum[i]了。
    }
    
    int main()
    {
    	init();
    	input_data();
    	solve(n, l, m);
    	output_ans();
    	return 0;
    }


  • 相关阅读:
    Week14
    带你一文读懂Batch Normalization
    019_04简易相机
    019_03camera的调用
    019_02视频播放之VideoView
    019_01播放视频之SurfaceView
    018_04音效播放之MediaPlayer与SoundPool
    018_03简易美图
    018_02图片水印
    018_01图片特效
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632323.html
Copyright © 2011-2022 走看看