Link:http://acm.hdu.edu.cn/showproblem.php?pid=6092
题意:给定
n
,
完全没有想明白是一个背包思想的题,所以就着题解来理解一下这个题的思路。
首先对于给定b数组,我们每次寻找到除了
b0
以外最小的一个不为0的数i,这个数的数字一定是a中确定的数,然后每次找到i后,删去这个数对于整个b数组的影响。
如第二个样例
B: 1 3 3 1
选定i = 1删去1后:(1加入A数组)
对于 b1 ,则减少了一个1 因此变为 b1=b1−b0=2
对于 b2 ,2可以由已经上一步中的 b1+1 得到,所以相当于要减去整个 b1 带来的影响,因此 b2=b2−b1=1
对于 b3 ,3可以有上一步的 b2+1 得到,所以要减去整个 b2 带来的影响,因此 b3=b3−b2=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=b1−b0=1
对于 b2 ,与之前的想法相同, b2=b2−b1=0此时B:1 1 0 0(为A:1时的B数组)
选定i = 1 删去 1:(1加入A数组)
修改 b1 ,则减少了一个1 因此变为 b1=b1−b0=0
后面的修改后依旧为0
此时A数组中刚好有n个数,并且B数组除 b0 外全为0,无法继续修改,因此输出结果:1 1 1
对于第一个样例
B: 1 1 1 1
选定i = 1删去1后:(1加入A数组)
对于 b1 ,则减少了一个1 因此变为 b1=b1−b0=0
对于 b2 , b2=b2−b1=1
对于 b3 , b3=b3−b2=0此时B:1 0 1 0(刚好是A : 2 时的B数组)
选定i = 2删去2:(2加入A数组)
对于 b2 ,则减少一个2 因为此时的 b2=b2−b0=0
对于 b3 ,因为这个 b3 我们在之前的一步中已经考虑过1对它的影响,这个则只需要考虑2的影响,又因为2+1=3,所以如果要减去2对3的影响,就是 b3 减去 b1 的影响,因此 b3=b3−b1=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]);
}
}
}