18.10.16 队测
T1 :
求给定集合有多少个非空子集可以分割成两个集合,使得它们的和相等。
其中:n≤20 , a[i]≤1e8
题目可以转化成:给每个数前添加一个系数p\in[-1,1],使得和为0的方案数。
由于n比较小,所以可以考虑爆搜。朴素的爆搜可以枚举每个数不选,第一个集合,第二个集合,复杂度O(3^n) 。
然后发现根据套路,可以想到折半搜索(meet~ in~ the ~middle)
先爆搜前十个的状态和sum,然后爆搜后十个进行匹配,我这里用的是map.
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using std :: vector ;
using std :: map;
using std :: clock;
#define ull unsigned long long
#define max(a,b) (a<b?b:a)
#define min(a,b) (a<b?a:b)
#define swap(x,y) x^=y^=x^=y
#define isdigit(ch) (ch>='0'&&ch<='9')
#define abs(x) (x<0?-x:x)
#define pb push_back
const int mod = 1e9+7;
inline void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
inline void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
inline void write(int x) {print(x);puts("");}
int n,a[30],sum1,sum2,sum[30],ans;
namespace task1 {
int Map[2000000],sta;
void dfs(int x) {
if(x>n) {if(!sum1&&!Map[sta]) Map[sta]=1,ans++;return ;}
if(sum[x]+sum1<0) return ;
if(-sum[x]+sum1>0) return ;
sum1+=a[x];dfs(x+1);sum1-=a[x];
sum1-=a[x];dfs(x+1);sum1+=a[x];
sta^=1<<(x-1);dfs(x+1);sta^=1<<(x-1);
}
void solve() {dfs(1);write(ans-1);}
}
namespace task2 {
struct node {map<int,int > s;vector<int > v;int num;};
map <int,node > mp;
map <int,int > mp2[5005];
int sum=0,ans=0,sta=0,Map[5050];
void dfs1(int x) {
if(x>10) {
if(!mp[sum].s[sta]) mp[sum].s[sta]=1,mp[sum].v.pb(sta),mp[sum].num++;
return ;
}
sum+=a[x];dfs1(x+1);sum-=a[x];
sum-=a[x];dfs1(x+1),sum+=a[x];
sta^=1<<(x-1);dfs1(x+1);sta^=1<<(x-1);
}
map<int,int > check[5005];
void dfs(int x) {
if(x>n) {
if(check[sta][-sum]) return ;
check[sta][-sum]=1;
int num=mp[-sum].num;
for(int i=0;i<num;i++)
if(!mp2[sta][mp[-sum].v[i]]) mp2[sta][mp[-sum].v[i]]=1,ans++;
return ;
}
sum+=a[x];dfs(x+1);sum-=a[x];
sum-=a[x];dfs(x+1),sum+=a[x];
sta^=1<<(x-10);dfs(x+1);sta^=1<<(x-10);
}
void solve() {
dfs1(1);for(int i=0;i<=5000;i++) Map[i]=0;sum=sta=0;
dfs(11);write(ans-1);
}
}
int main() {
// freopen("subsets.in","r",stdin);
// freopen("subsets.out","w",stdout);
read(n);for(int i=1;i<=n;i++) read(a[i]);
for(int i=n;i;i--) sum[i]=sum[i+1]+a[i];
if(n<=10) task1 :: solve();
else task2 :: solve();//write(clock());
return 0;
}
(emacs缩进有点问题...)
T2:
给定一个(0,1,2,..,n-1)的排列p,然后求一个(0,1,2,..,n-1)的排列q的个数
其中,q要满足 : 对排列s=(0,1,2,3,...,n-1)进行n-1次交换,每次交换s[q[i]],s[q[i]+1] 最后要满足s=p; 答案对1e9+7取模
由于每个位置只能swap一次,所以q的某些相邻位置的值的大小关系是可以确定的。
这就是一个简单的dp问题,设f[i][j]位前i个数第i的大小排名位j,然后前缀和优化即可。
#pragma GCC optimize(3)
#include<cstdio>
#define isdigit(ch) (ch>='0'&&ch<='9')
const int mod = 1e9+7;
inline void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
inline void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
inline void write(int x) {print(x);puts("");}
#define maxn 5050
#define end return puts("0"),0
int f[maxn][maxn],g[maxn][maxn],p[maxn],tmp[maxn],n;
int main() {
read(n);for(int i=0;i<n;i++) read(p[i]);
for(int i=0;i<n;i++)
if(p[i]>i) {
if(i>=1) if(tmp[i-1]==1) end;tmp[i-1]=2;
for(int j=i;j<p[i]-1;j++) if(tmp[j]==2) end;else tmp[j]=1;
if(p[i]<=n-2) if(tmp[p[i]-1]==1) end;else tmp[p[i]-1]=2;
}
else if(p[i]<i) {
if(p[i]>=1) if(tmp[p[i]-1]==2) end;tmp[p[i]-1]=1;
for(int j=p[i];j<=i-2;j++) if(tmp[j]==1) end;else tmp[j]=2;
if(i<=n-2) if(tmp[i-1]==2) end;else tmp[i-1]=1;
} else end;
f[0][1]=g[0][1]=1;
for(int i=1;i<n-1;i++)
for(int j=1;j<=i+1;j++) {
if(tmp[i-1]!=2) f[i][j]=(f[i][j]+g[i-1][j-1])%mod;
if(tmp[i-1]!=1) f[i][j]=(f[i][j]+g[i-1][i]-g[i-1][j-1])%mod;
g[i][j]=(g[i][j-1]+f[i][j])%mod;
}
int ans=0;
for(int i=1;i<=n-1;i++) ans=(ans+f[n-2][i])%mod;
write((ans%mod+mod)%mod);
return 0;
}
T3 :
给出一个数组a ,有k个背包,每个背包能放连续的一段数,然后任取一个数x\in[0,P-1],给出一个P,对于每个a[i],a[i]=(a[i]+x)\%P,最小化背包存的东西的最大值。
其中:P\leq 1e4,n\leq1e4
sample input :
5 5 2
0 4 2 1 3
sample output :
5
hint : x=3,a={3,2,0,4,1},最大值为5.
首先很容易想到,先暴力枚举x,然后二分答案,O(Pn log n).
然后(看了题解才知道)可以用随机化优化时间复杂度。
随找出一个x,然后check一下最优解,如果不能更优,则continue,否则二分。
时间复杂度O(nP+nlognlogP).
#pragma GCC optimize(3)
#include<cstdio>
#include<ctime>
#include<algorithm>
using std :: random_shuffle;
using std :: clock;
#define ull unsigned long long
#define max(a,b) (a<b?b:a)
#define min(a,b) (a<b?a:b)
#define swap(x,y) x^=y^=x^=y
#define isdigit(ch) (ch>='0'&&ch<='9')
#define abs(x) (x<0?-x:x)
#define pb push_back
const int mod = 1e9+7;
inline void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
inline void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
inline void write(int x) {print(x);puts("");}
int n,P,k,a[100050],ans=1e9,g[100040],b[100020];
int check(int x) {
int res=0,num=1;
for(int i=1;i<=n;i++)
if(a[i]>x) return false;
else if(res+a[i]>x) res=a[i],num++;
else res+=a[i];
return num<=k;
}
int main() {
read(n),read(P),read(k);
for(int i=1;i<=n;i++) read(b[i]),b[i]%=P;
for(int i=1;i<=P;i++)
g[i]=i%P;
random_shuffle(g+1,g+P+1);
for(int i=1;i<=P;i++) {
for(int j=1;j<=n;j++) a[j]=(b[j]+g[i])%P;
if(!check(ans)) continue;
int l=0,r=ans,res=1e8;
while(l<=r) {
int mid=(l+r)>>1;//write(mid);
if(check(mid)) res=mid,r=mid-1;
else l=mid+1;
}
ans=min(ans,res);
}write(ans);
return 0;
}
__EOF__
作 者:Hyscere
出 处:https://www.cnblogs.com/hbyer/p/9799685.html
关于博主:编程路上的小学生,热爱技术,喜欢专研。评论和私信会在第一时间回复。或者直接私信我。
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 手把手教你更优雅的享受 DeepSeek
· AI工具推荐:领先的开源 AI 代码助手——Continue
· 探秘Transformer系列之(2)---总体架构
· V-Control:一个基于 .NET MAUI 的开箱即用的UI组件库
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现