Jellyfish and Green Apple
本题要求将n个苹果,每次可以将一个苹果分成两半。要操作几次可以均分给m个小朋友。
首先不考虑最少操作次数,本题求出n和m的最小公倍数就可以实现均分。也就是每位小朋友可以得到gcd(n,m)/m个苹果片。每个苹果被分成了gcd(n,m)/n份。那么从被分成的份数中可以得到分成的份数必须是2的次方数。所以在这里通过b&(b-1)去除数中的最后一个1,也可以用来检测是不是2的倍数。
然后这是不考虑最小操作数的情况。考虑最小操作数就是将每位小朋友首先的苹果片给组合起来。但注意要2进制的组合。比如2可以组合。4可以组合。8可以组合。
最后将所有得苹果片数求出来来,计算多出来的苹果片数就是切了多少刀,因为每切一刀就增加一个苹果片。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int gcd(int a, int b)
{
if (b==0) return a;
return gcd(b, a%b);
}
void solve()
{
int n, m;
cin>>n>>m;
n %= m;
if (!n)
{
cout<<0<<endl;
return ;
}
int gd = gcd(n, m);
int bei = n*m/gd;
int x1 = bei/n;
if (x1&(x1-1))
{
cout<<-1<<"\n";
return ;
}
// cout<<bei<<endl;
int avg = bei/m;
int num = 0;
while (avg)
{
++num;
avg = (avg&(avg-1));
// cout<<"avg="<<avg<<endl;
}
// cout<<"num="<<num<<endl;
cout<<num*m-n<<endl;
}
signed main()
{
int t;
cin>>t;
while (t--)
{
solve();
}
return 0;
}
Jellyfish and Mex
本题的MEX代表不在数组里面的最小非负整数。每次操作会删除一个数组元素,然后每次会取MEX加到m里面。问最后m可以是多小。
本题直接做得话删除并没有什么规律,也不是每次删最小的就是最优的选择。这个在题目中给的例子就可以看出来。
那么
- 如果可以将MEX值的变化找到状态关系,那么就可以使用动态规划求解。
- 对于MEX值的状态来说,其实取决于比他大的MEX然后经历了多少次操作。
- dp[i] = min(dp[i], dp[j]+(cnt[i]-1)*j+i);
//如果可以将MEX值的变化找到状态关系,那么就可以使用动态规划求解。
//对于MEX值的状态来说,其实取决于比他大的MEX然后经历了多少次操作。
//dp[i] = min(dp[i], dp[j]+(cnt[i]-1)*j+i);
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 5000+10;
int n;
int a[maxn];
map<int, int> cnt;
int dp[maxn];
void solve()
{
cnt.clear();
memset(a, 0, sizeof(a));
int m = 0;
memset(dp, 127, sizeof dp);
cin>>n;
for (int i=0;i<n;i++)
{
cin>>a[i];
m = max(m, a[i]);
cnt[a[i]]++;
}
int pos = 0;
while (cnt[pos]>=1) pos++;
if (!pos)
{
cout<<"0\n";
return ;
}
dp[pos] = 0;
int ans = (m+1)*n;
for (int i=pos-1;i>=0;i--)
{
dp[i] = (m+1)*n;
for (int j=i+1;j<=pos;j++)
{
// cout<<"i="<<i<<" dp[i]="<<dp[i]<<endl;
dp[i] = min(dp[i], dp[j]+(cnt[i]-1)*j+i);
}
}
printf("%lld\n", dp[0]);
}
signed main()
{
int t;
cin>>t;
while (t--)
{
solve();
}
return 0;
}