一道状压
题目大意#
给定两个长度为 的数组 和 ,可以进行若干次如下操作:
- 选择两个数 ,将( 或 )与( 或 )交换。
问最少进行多少次上述操作,可使得对于每个 ,都有 。
问题转化#
首先我们用状态压缩的思想,用状态的第 位表示 是()否()有 。
我们可以把一个个状态都看作点,如果两个状态之间可以通过调换牙刷互相得到,那么这两个点间就有一条边。
和 在边权相同的图上可以直接用 求单源最短路 同样的道理,这样在建出的图上跑一遍 即可,起点是初始状态。
但是到达每个点时的 数组和 数组会互不相同,所以我们用 和 记录到达点 (状态为 )时,第 把牙刷的两个属性值,这样的话就可以根据 和 判断两个点之间是否能互相到达。
我们并不需要真正地去建图,真正去建图的话很麻烦。可以在 的过程中,对于当前(队头)的状态,枚举交换哪两个,然后让交换后的状态入队即可。
DP 求解#
至于 中的 部分(求最短路,但还是更像状压 一点),状态转移方程如下:
设 为当前状态, 表示 是()否()有 , 表示到达状态 至少要交换几次。
现在对 进行交换操作
-
若交换后 发生改变,而 未发生改变,则
dp[mask^(1<<i)]=dp[mask]+1
。 -
若交换后 发生改变,而 未发生改变,则
dp[mask^(1<<j)]=dp[mask]+1
。 -
若交换后 和 均发生改变,则
dp[mask^(1<<i)^(1<<j)]=dp[mask]+1
。
那么如何判断交换后 和 是否发生变化呢?
比如交换 和 ,若(abs(a1[mask][i]-b1[mask][i])<=c)^(abs(a1[mask][j]-b1[mask][i])<=c)==1
,则 发生变化。同样的,若(abs(a1[mask][j]-b1[mask][j])<=c)^(abs(a1[mask][i]-b1[mask][j])<=c)==1
,则 发生变化。
那么如果交换 和 ,若(abs(a1[mask][i]-b1[mask][i])<=c)^(abs(b1[mask][j]-b1[mask][i])<=c)==1
,则 发生变化。同样的,若(abs(a1[mask][j]-b1[mask][j])<=c)^(abs(a1[mask][j]-a1[mask][i])<=c)==1
,则 发生变化。
不难发现,讨论以上两种情况其实就足够了,没必要再用 去交换。
举个例子,。那么上面的第一种实质上是让 一组, 一组。第二种是让 一组, 一组。此时就已经包含了所有的情况了,不需要让 去和 或 交换。
代码如下:
#include<bits/stdc++.h>
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define reg register
using namespace std;
inline int read()
{
int x=0;
short f=1;
char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int n,c,dp[1<<20],a[20],b[20],a1[1<<20][20],b1[1<<20][20],mask;
bool vis[1<<20];
inline void memcopy(int x) {for(reg int i=0;i<n;i=-~i) a1[x][i]=a1[mask][i],b1[x][i]=b1[mask][i];}
inline void DP()
{
queue<int>q;
q.push(mask);
while(!q.empty()&&!dp[(1<<n)-1])//小优化,如果dp[(1<<n)-1]已被求出就直接结束
{
mask=q.front();
q.pop();
for(reg int i=0;i<n;i=-~i/*位运算的i++*/)
for(reg int j=0;j<i;j=-~j)//i和j 枚举交换哪两个
{
int nxt1=mask^(1<<j),nxt2=mask^(1<<i),nxt3=mask^(1<<i)^(1<<j);
if((abs(a1[mask][i]-b1[mask][i])<=c)^(abs(a1[mask][j]-b1[mask][i])<=c))//flag[i]发生变化
{
if((abs(a1[mask][j]-b1[mask][j])<=c)^(abs(a1[mask][i]-b1[mask][j])<=c))//flag[j]发生变化
{
if(!vis[nxt3])
{
dp[nxt3]=dp[mask]+1;vis[nxt3]=1;q.push(nxt3);
memcopy(nxt3);swap(a1[nxt3][i],a1[nxt3][j]);
//flag[i]和flag[j]均发生变化
}
}
else if(!vis[nxt2])
{
dp[nxt2]=dp[mask]+1;vis[nxt2]=1;q.push(nxt2);
memcopy(nxt2);swap(a1[nxt2][i],a1[nxt2][j]);
//仅flag[i]发生变化
}
}
else if((abs(a1[mask][j]-b1[mask][j])<=c)^(abs(a1[mask][i]-b1[mask][j])<=c)&&!vis[nxt1])//flag[j]发生变化
{
dp[nxt1]=dp[mask]+1;vis[nxt1]=1;q.push(nxt1);
memcopy(nxt1);swap(a1[nxt1][i],a1[nxt1][j]);
//仅flag[j]发生变化
}
//1.exchange a[i] and a[j]
if((abs(a1[mask][i]-b1[mask][i])<=c)^(abs(b1[mask][j]-b1[mask][i])<=c))
{
if((abs(a1[mask][j]-b1[mask][j])<=c)^(abs(a1[mask][j]-a1[mask][i])<=c))
{
if(!vis[nxt3])
{
dp[nxt3]=dp[mask]+1;vis[nxt3]=1;q.push(nxt3);
memcopy(nxt3);swap(a1[nxt3][i],b1[nxt3][j]);
}
}
else if(!vis[nxt2])
{
dp[nxt2]=dp[mask]+1;vis[nxt2]=1;q.push(nxt2);
memcopy(nxt2);swap(a1[nxt2][i],b1[nxt2][j]);
}
}
else if((abs(a1[mask][j]-b1[mask][j])<=c)^(abs(a1[mask][j]-a1[mask][i])<=c)&&!vis[nxt1])
{
dp[nxt1]=dp[mask]+1;vis[nxt1]=1;q.push(nxt1);
memcopy(nxt1);swap(a1[nxt1][i],b1[nxt1][j]);
}
//2.exchange a[i] and b[j]
}
}
}
signed main()
{
n=read();c=read();
for(reg int i=0;i<n;i=-~i)
{
a[i]=read();b[i]=read();
if(abs(a[i]-b[i])<=c) mask|=(1<<i);
}
dp[mask]=0;vis[mask]=1;
for(reg int i=0;i<n;i=-~i) a1[mask][i]=a[i],b1[mask][i]=b[i];
DP();
return printf("%d",dp[(1<<n)-1]),0;
}
时间复杂度#
。
枚举交换哪两个()。
将原来的所对应的属性值数组 赋值给 交换后的所对应的属性值数组 。
总复杂度 ,化简得 ,约 ,完全能过。
但实际复杂度是达不到的,因为几乎不会出现 每个状态都能到达 的情况,而且我们还有小优化。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】