10.16 多校联测
考虑折半搜索,每个数的系数只能是-1,0,1之中的一个,因此可以先通过的搜索分别搜索出两边每个状态的和以及数字的选择情况。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 5 int n,mid,ans,a[30],num,tmp[1<<11]; 6 int tot=1,pre[60010],son[60010],now[1<<11]; 7 bool b[1<<21]; 8 9 int cnt; 10 struct Node{int sum,set;}f[60010]; 11 12 bool cmp(Node a,Node b){return a.sum<b.sum;} 13 14 void DFS1(int dep,int sum,int set){ 15 if(dep==mid+1){ 16 tot++;pre[tot]=now[set]; 17 now[set]=tot;son[tot]=sum; 18 } 19 else{ 20 DFS1(dep+1,sum,set); 21 DFS1(dep+1,sum+a[dep],set|(1<<(dep-1))); 22 DFS1(dep+1,sum-a[dep],set|(1<<(dep-1))); 23 } 24 } 25 26 void DFS2(int dep,int sum,int set){ 27 if(dep==n+1){ 28 ++cnt; 29 f[cnt].sum=sum;f[cnt].set=set; 30 } 31 else{ 32 DFS2(dep+1,sum,set); 33 DFS2(dep+1,sum+a[dep],set|(1<<(dep-1))); 34 DFS2(dep+1,sum-a[dep],set|(1<<(dep-1))); 35 } 36 } 37 38 int main(){ 39 scanf("%d",&n);mid=n/2; 40 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 41 DFS1(1,0,0);DFS2(mid+1,0,0); 42 sort(f+1,f+cnt+1,cmp); 43 for(int i=0;i<=(1<<mid)-1;i++){ 44 num=0; 45 for(int p=now[i];p;p=pre[p]) 46 tmp[++num]=son[p]; 47 sort(tmp+1,tmp+num+1); 48 for(int j=1,k=1;j<=cnt;j++){ 49 while(k<=num&&tmp[k]<f[j].sum) 50 k++; 51 if(k==num+1)break; 52 if(tmp[k]==f[j].sum) 53 b[i|f[j].set]=1; 54 } 55 } 56 for(int i=1;i<=(1<<n)-1;i++) 57 if(b[i])ans++; 58 printf("%d\n",ans); 59 return 0; 60 }
T2 毛二琛
考虑排列p中的每个数怎样才能被移动到该到的地方。 问题转化为一个大小为n-1的排列,某些地方限定了相邻两数的大小关系。 直接DP即可,fi表示前i个数,第i个数在前i个数中是第j小的。前缀和优化可以做到O(n^2)
T3 毛三琛
暴力枚举x然后二分最大重量,复杂度为O(nPlogn)。
考虑对x枚举的顺序随机一下,这样枚举到一个x时候,可以先去check一下是否比目前的最优解要优,如果是,那么再去二分,否则直接continue。 由于一个随机排列中比前面所有数都大的数的数量期望为log,所以复杂度为O(nP+nlognlogP)。
我不会随机算法,考场的时候只拿了暴力分。。哭晕