HDU 6092 Rikka with Subset(题解解释)

来源:互联网 发布:mac开发网站 编辑:程序博客网 时间:2024/06/05 09:57

Link:http://acm.hdu.edu.cn/showproblem.php?pid=6092

题意:给定nmb0~bm,要求求出集合A:a1~an,集合A中每个子集的和i的个数为bi,输出字典序最小的A。

完全没有想明白是一个背包思想的题,所以就着题解来理解一下这个题的思路。
首先对于给定b数组,我们每次寻找到除了b0以外最小的一个不为0的数i,这个数的数字一定是a中确定的数,然后每次找到i后,删去这个数对于整个b数组的影响。
如第二个样例

B: 1 3 3 1
选定i = 1删去1后:(1加入A数组)
对于b1 ,则减少了一个1 因此变为 b1=b1b0=2
对于b2,2可以由已经上一步中的b1+1得到,所以相当于要减去整个b1 带来的影响,因此b2=b2b1=1
对于b3,3可以有上一步的b2+1得到,所以要减去整个b2带来的影响,因此b3=b3b2=0
b3的处理过程中,我们毋须再考虑是否会有1 1 1这种情况导致的3,因为上一步的2中就已经整个包括了1 1 和 2这种情况,删去影响时删去整个的b2即可。

此时B:1 2 1 0(刚好是A : 1 1时的B数组)
选定i = 1删去1:(1加入A数组)
对于b1 ,则减少了一个1 因此变为 b1=b1b0=1
对于b2,与之前的想法相同,b2=b2b1=0

此时B:1 1 0 0(为A:1时的B数组)
选定i = 1 删去 1:(1加入A数组)
修改b1 ,则减少了一个1 因此变为 b1=b1b0=0
后面的修改后依旧为0
此时A数组中刚好有n个数,并且B数组除b0 外全为0,无法继续修改,因此输出结果:1 1 1

对于第一个样例

B: 1 1 1 1
选定i = 1删去1后:(1加入A数组)
对于b1 ,则减少了一个1 因此变为 b1=b1b0=0
对于b2b2=b2b1=1
对于b3b3=b3b2=0

此时B:1 0 1 0(刚好是A : 2 时的B数组)
选定i = 2删去2:(2加入A数组)
对于b2,则减少一个2 因为此时的 b2=b2b0=0
对于b3,因为这个b3我们在之前的一步中已经考虑过1对它的影响,这个则只需要考虑2的影响,又因为2+1=3,所以如果要减去2对3的影响,就是b3减去b1的影响,因此b3=b3b1=0
此时A数组中刚好有n个数,并且B数组除b0 外全为0,无法继续修改,因此输出结果:1 2

这个消去影响的思想主要是一种背包的思想,即每次只选择去拿一个数,考虑这个数对它之后的数会产生的影响。
对于一个3,第一次因为2+1=3,那么对于它来说,1个1的影响是由2产生的,则减去2的数量即可;第二次1+2=3,对于它来说一个2的影响只会是1产生的,则减去1的数量即可。那么这里会不会影响重复呢?答案是不会,因为第二次的时候,1的数量是肯定为0的,这样会保证不重复减。

如何和背包的思想结合还是等我看了翻货币问题再说说看吧~
贴代码:

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <vector>#include <queue>#include <cmath>using namespace std;const int N = 1E4 + 10;typedef long long ll;ll b[N];ll a[55];int main(){    int t;    scanf("%d", &t);    while(t--){        int n, m;        scanf("%d %d", &n, &m);        for(int i = 0; i <= m; i++){            scanf("%lld", &b[i]);        }        int cnt = 0;        for(ll i = 1; i <= m; i++){            if(b[i]){                if(cnt == n) break;                int num = b[i];                for(ll j = 0; j < num; j++){                    a[cnt++] = i;                    for(ll k = i; k <= m; k++){                        b[k] -= b[k-i];                    }                }            }        }        for(int i = 0; i < cnt; i++){            if(i == cnt - 1) printf("%lld\n", a[i]);            else printf("%lld ", a[i]);        }    }}
原创粉丝点击