标签:集中 pen 限制 void div span false 个数 ace
题目大意
给出一个由N个整数构成的集合{ai}和一个整数K,若该集合的某个非空子集中的所有元素之和大于等于K,则称该子集是good的
若去掉一个数不会对good的集合的个数产生影响,则称该数字为unnecessary的
请求出在N个数中unnecessary的数的个数
N,K≤5000,ai≤10^9
题解
正难则反,考虑什么时候一个数是necessary的。
如果存在一个子集,这个子集中所有数之和小于K,但把ai加入子集后所有数之和大于等于K,那么ai是necessary的。
那么对于每个ai,我们只需要去考虑能不能找到这样一个子集,如果找不到,ai就是unnecessary的。
不难发现,如果ai≥K,或者某个子集中所有数之和≥K,这些都是无意义的。
如果给你N个数的集合S,问你它的子集的元素和有哪些可能,这是很好求的,开个bool数组vis,每加入一个数a[j],若vis[i]==true,则vis[i+a[j]]=true即可。
但是这里存在着限制,我们当前在考虑除了ai剩下的数相加能否小于K,且加上ai后大于等于K,你不能把ai也算进去。
所以我们设Pre[i][j]表示从第一个数到第i个数这个前缀的vis,设Suf[i][j]表示从第N个数到第i个数这个后缀的vis。
那么考虑ai时,我们只需考虑能否用Pre[i-1][j]和Suf[i+1][k]凑出一个小于K的数,它加上ai大于等于K
贪心地想,我们凑出的这个数一定要在小于K的情况下最大
暴力去找的话每次是K^2的,找N次,时间复杂度是O(N*K^2)的,显然会超时。
考虑尺取法的思想,维护两个指针p1,p2,p1指向Pre[i-1],p2指向Suf[i+1]
p1从K-1往0向前移动,p2从0往k-1向后移动
对于一个固定的p1,p2一直往后移动,直到p1+p2≥K,p2停止移动,然后去移动p1
因为p1向前移动,移动后p1一定会变小,所以p2继续向后移动
如果p1+p2<K,且Pre[i-1][p1]==true,Suf[i+1][p2]==true,p1+p2+ai≥K,则ai是necessary的
这样的话p1,p2的移动都是单调的,单次查找是O(K)的,总时间复杂度是O(NK)的
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 using namespace std; 6 7 bool Pre[5002][5002],Suf[5002][5002],Ans[5002]; 8 int Data[5005]; 9 int N,K; 10 11 template<typename elemType> 12 inline void Read(elemType &T){ 13 elemType X=0,w=0; char ch=0; 14 while(!isdigit(ch)) {w|=ch==‘-‘;ch=getchar();} 15 while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); 16 T=(w?-X:X); 17 } 18 19 int main(){ 20 Read(N);Read(K); 21 for(register int i=1;i<=N;++i) 22 Read(Data[i]); 23 sort(Data+1,Data+N+1); 24 Pre[0][0]=Suf[N+1][0]=true; 25 for(register int i=1;i<=N;++i){ 26 for(register int j=0;j<K;++j){ 27 Pre[i][j]=Pre[i-1][j]; 28 Suf[N-i+1][j]=Suf[N-i+2][j]; 29 } 30 for(register int j=0;j<K;++j){ 31 if(Suf[N-i+2][j] && j+Data[N-i+1]<K) Suf[N-i+1][j+Data[N-i+1]]=true; 32 if(Pre[i-1][j] && j+Data[i]<K) Pre[i][j+Data[i]]=true; 33 } 34 } 35 for(register int i=1;i<=N;++i){ 36 if(Data[i]>=K){Ans[i]=true;continue;} 37 int p1=K-1,p2=0; 38 bool flag=false; 39 while(p1>=0 && p2<K){ 40 if(!Pre[i-1][p1]){--p1;continue;} 41 while(p1+p2<K){ 42 if(Pre[i-1][p1] && Suf[i+1][p2] && p1+p2+Data[i]>=K){ 43 Ans[i]=true; 44 flag=true; 45 break; 46 } 47 ++p2; 48 } 49 if(flag) break; 50 --p1; 51 } 52 } 53 int Count=0; 54 for(register int i=1;i<=N;++i) 55 if(!Ans[i]) ++Count; 56 printf("%d\n",Count); 57 58 return 0; 59 }
标签:集中 pen 限制 void div span false 个数 ace
原文地址:https://www.cnblogs.com/AEMShana/p/12209767.html