【BZOJ2612】同余类BFS
本题的算法比较经典,就直接标题写上了,刚在Luogu上遇到了一道很类似的题,一下子竟然没想起来,,略回忆一下一下。
BZOJ2612(权限)2612: [Poi2003]Sums
Description
我们给定一个整数集合A. 考虑一个非负整数集合A', 所有属于A' 的集合的数x满足当且仅当能被表示成一些属于A 的元素的和(数字可重复). 比如, 当 A = {2,5,7}, 属于A' 的数为: 0 (0个元素的和), 2, 4 (2 + 2) and 12 (5 + 7 or 7 + 5 or 2 + 2 + 2 + 2 + 2 + 2); 但是元素1和3不属于A'.
Input
第一行有一个整数n: 代表集合 A的元素个数, 1 <= n <= 5000. 接下来每行一个数ai描述一个元素, 1 <= ai <= 50000. A = {a1, a2, ..., an}, a1 < a2 < ... < an.
接下来一个整数k, 1 <= k <= 10000. 每行一个0 到 1000000000, 分别代表b1, b2, ..., bk.
Output
输出k行. 第i行打印TAK, 如果 bi 属于A', 否则打印NIE.
Sample Input
3
2
5
7
6
0
1
4
12
3
2
Sample Output
TAK
NIE
TAK
TAK
NIE
TAK
一看,WHAT?莫非是背包?再一看,诶,这数据范围怎么这么诡异。。
好吧,其实本质也是背包。我们如果把最小的那个物品当做模数,剩下的就在同余意义下跑BFS(其实就是背包),我们只需要找到在模最小物品意义下找到对于一个余数第一个满足的物品总和大小(最小的那个),之后对于同余意义下的其他更大的物品,我们只要往其上一直叠加最小物品即可。最后判断两个之间的大小就可以啦。
code:
#include<bits/stdc++.h> using namespace std; const int maxn = 5005; const int maxx = 50005; int n; int a[maxn]; int dis[maxx]; queue<int>q; bool rd[maxx]; void spfa() { for(int i=0;i<a[1];i++) { dis[i] = 0x3f3f3f3f; } dis[0] = 0; q.push(0); rd[0] = 1; while(q.size()) { int x = q.front(); q.pop(); rd[x] = 0; for(int i = 1; i <= n; i++) { if( dis[(a[i]+x)%a[1]] > dis[x] + a[i] ) { dis[(a[i]+x)%a[1]] = dis[x] + a[i]; if(!rd[(a[i]+x)%a[1]]) { q.push((a[i]+x)%a[1]); rd[(a[i]+x)%a[1]]=1; } } } } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } spfa(); int k; scanf("%d",&k); for(int i=1;i<=k;i++) { int x; scanf("%d",&x); if(dis[x%a[1]]<=x) { printf("TAK\n"); } else printf("NIE\n"); } }