问题 A: 毛一琛/$cow$ $subsets$
时间限制: 1 Sec 内存限制: 512 MB
题面
题面谢绝公开。
题解
题名貌似是个大神??看起来像是签到题然后就死了。
首先$O(3^n)$算法显然。也显然过不去$20$的测试点。
正解是赫赫有名的$meet$ $in$ $the$ $middle$算法。数据在40以内的都能用$meet$ $in$ $the$ $middle$??
对于两半路径,可以拼起来并且构成合法答案的条件是两人获得的分数相同。
所以一个比较聪明的办法是,不去记录两个人各自得到了多少分,而是记录两个人得分的差。
至于谁减谁无所谓,相同的话把其中一个翻转就好了。不搞相反也可以避免重复。
于是我们开一个$map$,对于每一个在前一半路径得到的差值映射一个编号。并以之为下标存储前一半扫过的状态,方便拼接。
对于后一半路径扫过的差值,在$map$里查询,有就拼上,状态置1。
代码:
#include<bits/stdc++.h> #define rint register int using namespace std; int n,a[23],tot,ans; bool vis[(1<<21)+5]; map <int,int> m; vector <int> v[10000005]; inline void dfs1(rint k,rint res,rint con) { if(k==n/2+1) { if(m.find(res)==m.end())m[res]=++tot; int lin=m[res];v[lin].push_back(con); return ; } dfs1(k+1,res,con); dfs1(k+1,res+a[k],con|(1<<k-1)); dfs1(k+1,res-a[k],con|(1<<k-1)); return ; } inline void dfs2(rint k,rint res,rint con) { if(k==n+1) { if(m.find(res)!=m.end()) { int lin=m[res]; for(rint i=0;i<v[lin].size();++i) vis[v[lin][i]|con]=1; } return ; } dfs2(k+1,res,con); dfs2(k+1,res-a[k],con|(1<<k-1)); dfs2(k+1,res+a[k],con|(1<<k-1)); return ; } int main() { scanf("%d",&n); for(rint i=1;i<=n;++i)scanf("%d",&a[i]); dfs1(1,0,0);dfs2(n/2+1,0,0); for(rint i=1;i<=(1<<n)-1;++i)ans+=vis[i]; printf("%d\n",ans); return 0; }
原文地址:https://www.cnblogs.com/xingmi-weiyouni/p/11666899.html
时间: 2024-11-06 07:25:37