AtCoder Beginner Contest 270
D
贪心是错的,考虑 dp。
设 \(dp[i]\) 表示还剩 \(i\) 个石子,先手走能取到的最大石子数。
转移:\(dp[i]=\max(dp[i],i-dp[i-a[j]])\)
其中 \(j\) 为枚举的本轮取几个石子。
感觉这个 dp 挺不好想的,以后对于这种博弈 dp 的方程要敏感。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,k,a[N],f[N];
signed main()
{
cin>>n>>k;
for(int i=1;i<=k;++i)cin>>a[i];
for(int i=1;i<=n;++i)
for(int j=1;j<=k;++j)
if(i-a[j]>=0)f[i]=max(f[i],i-f[i-a[j]]);
cout<<f[n];
return 0;
}
E
看到 \(k\) 这么大,容易想到二分。
二分能取多少整轮,对于多余的部分模拟即可。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,k,a[N],cnt[N];
bool check(int mid)
{
int res=0;
for(int i=1;i<=n;++i)
if(a[i]>mid)res+=mid;
else res+=a[i];
return res<=k;
}
signed main()
{
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>a[i];
int l=0,r=k+1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
int res=0;
for(int i=1;i<=n;++i)
{
if(a[i]>l)res+=l;
else res+=a[i];
a[i]=max(a[i]-l,0ll);
}
res=k-res;
for(int i=1;i<=n;++i)
{
if(res==0)break;
if(a[i])res--,a[i]--;
}
for(int i=1;i<=n;++i)
cout<<a[i]<<" ";
return 0;
}