目录:
完全二叉树的权值
- 基本思想:完全二叉树的层序遍历
- 由于是完全二叉树,每层结点都是按顺序的,该层的结点数:
0 < = x < = 2 n − 1 , ( n 为层数) 0<=x<=2^{n-1},(n为层数) 0<=x<=2n−1,(n为层数)
如果该层结点数 x < 2 n − 1 x<2^{n-1} x<2n−1,则为最后一层
设每一层有num
个结点,k
为当前层级 - 扫描一遍序列即可,由于结点绝对值最大为
1
0
5
10^5
105,最多有
1
0
5
10^5
105个点,最后一层和可能爆
int
,所以这里用long long
. - 代码如下
#include <iostream>
using namespace std;
const int N=100010;
int a[N];
int n;
int main()
{
cin>>n;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
int d=0;
long long res=-0x3f3f3f3f;
for(int i=0,k=1,num=1;i<n;i+=num,k++,num*=2)
{
long long sum=0;
for(int j=i;j<i+num&&j<n;j++) sum+=a[j];//层序遍历
if(sum>res) res=sum,d=k;
}
cout<<d<<endl;
return 0;
}
外卖店优先级
- 同理,直接暴力会TLE
算法步骤:
- 根据订单时间对所有订单排序,为什么要排序呢,因为排序后处理每一个外卖店都是往后的时间,可以轻易得到上一次外卖店的订单时间
- 根据上一次的订单时间计算优先级,用数组
a
表示当前优先级数量,数组b
表示上一次的订单时间,st
标记该外卖点是否在缓存中 - 遍历所有订单,再处理最后一次时间
T
产生的优先级减少
- 代码如下
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int,int>PII;
#define t first
#define id second
const int N=100010;
int a[N],b[N];
int n,m,T;
PII s[N];
bool st[N];
int main()
{
cin>>n>>m>>T;
for(int i=0;i<m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
s[i]={a,b};
}
sort(s,s+m);
for(int i=0;i<m;i++)
{
int k=s[i].id;
if(b[k]!=s[i].t)//不是同一时间的订单
{
a[k]=max(0,a[k]-(s[i].t-b[k]-1));
if(a[k]<=3) st[k]=false;
//这里要注意判断一定要加在下一个语句前面,不要在后面
}
a[k]+=2;
if(a[k]>5) st[k]=true;
b[k]=s[i].t;
}
for(int i=1;i<=n;i++)
{
a[i]-=T-b[i];
if(a[i]<=3) st[i]=false;
}
int res=0;
for(int i=1;i<=n;i++) res+=st[i];
cout<<res<<endl;
return 0;
}
修改数组
- 这题容易想到的是直接模拟一遍,用一个
st
数组标记是否用过该数,依次扫描输入的数往下递推,但是这样是妥妥地TLE
.
因此我们考虑用另一种方法—并查集
算法步骤:
- 基本思路:用
p[x]
储存这个x
的下一个可用的数
- 对并查集初始化,
p[i]=i;
- 对输入的数进行判断,通过
find(x)
找到该数的下一个可用的数,最后这个数的p[x]=x+1
,表示x已经用过了,下一个只能用x+1
. - 每轮操作都输出数即可,也可以用离线做法
- 代码如下:
#include <iostream>
using namespace std;
const int N=100010,M=1100010;
int n;
int p[M];
int find(int x)
{
if(x==p[x]) return x;
return p[x]=find(p[x]);//并查集能行优化得益于这里
}
int main()
{
cin>>n;
for(int i=1;i<=M;i++) p[i]=i;//init();
for(int i=0;i<n;i++)
{
int x; scanf("%d",&x);
x=find(x);
printf("%d ",x);
p[x]=x+1;
}
return 0;
}
糖果
- 糖果种数少,每种糖果只有选了和未选两种状态,因此我们可以考虑状压dp,经过验证,可行!
- 如
1000011
1000011
1000011表示这种状态已经有了第
1
,
2
,
7
1,2,7
1,2,7这三种糖果,用
a
数组保存
既然是dp题,我们就直接上处理方法
状态表示:
f
[
i
]
f[i]
f[i]表示状态i
下的最少方案数
状态转移方程: f [ t ] = m i n ( f [ t ] , f [ j ] + 1 ) f[t]=min(f[t],f[j]+1) f[t]=min(f[t],f[j]+1)
结果表示: f [ ( 1 < < m ) − 1 ] f[(1<<m)-1] f[(1<<m)−1]
- 代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int M=1<<20;
int a[M],f[M];
int n,m,k;
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
{
for(int j=0;j<k;j++)//状态集合到a[i];
{
int x; scanf("%d",&x);
a[i]|=1<<x-1;//该包糖果的种类数累计
}
}
memset(f,0x3f,sizeof f);
f[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<1<<m;j++)
{
int t=j|a[i];
f[t]=min(f[t],f[j]+1);
}
}
if(f[(1<<m)-1]==0x3f3f3f3f) cout<<-1<<endl;
else cout<<f[(1<<m)-1]<<endl;
return 0;
}
组合数问题【待补】
未完待续…