A.炸鸡块哥哥的粉丝题
#include<bits/stdc++.h>
using namespace std;
int n;
string s;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>s;
for(int i=0;i<(n+1)/2;i++)
cout<<s[i];
return 0;
}
B.智乃想考一道鸽巢原理
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const int INF = 0x7fffffff;
const double eps = 1e-5;
int t,n,a[N];
int max1,max2,maxa,mi;
long long sum=0;
void solve() {
max1=max2=-INF;
sum=0;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
if(a[i]>max1){
max1=a[i];//求最大值
mi=i;
}
}
if(n==1) {//特判1
cout<<1<<endl;
return;
}
for(int i=0;i<n;i++){
if(i!=mi) max2=max(max2,a[i]);//求第二大值
}
for(int i=0;i<n;i++){//重点 鸽巢原理
maxa=(max1==a[i])?max2:max1;
if(sum-a[i]-maxa>=maxa-1) {
if(a[i]>((sum-a[i])%2)) cout<<1<<' ';
else cout<<0<<' ';
}
else {
if(a[i]>(2*maxa-sum+a[i])) cout<<1<<' ';
else cout<<0<<' ';
}
}
cout<<endl;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin>>t;
while(t--){
solve();
}
return 0;
}
C. 智乃想考一道完全背包(Easy version)
分析:因为要保证a1≤a2≤...≤aK≥...≥aN,所以当取除ak外的任意一件物品时,必须先取一件ak
换一句话来说,如果位置 k上的物品本来就很好了,那么肯定选这个物品就行了。但是如果有个很好的物品远离位置 k,那么我们就需要选上从这个位置到 k 上的所有物品,才能选上这一个物品,它就相当于和中间的物品进行捆绑销售了。
反正一定会捆绑销售,所以我们不如一开始就将 l ∼ k 或 k ∼ r位置上的物品进行绑定,把它们变成一个物品,然后跑完全背包。
不过由于我们选了 l ∼ k 的物品的话,我们还能去拓展另一边到 r rr,并省下一个 k。所以另一边我们也需要绑定过来,也就是说需要把 l ∼ r上的物品进行绑定。
如果我们以位置为横坐标,每个位置上物品选择的个数为纵坐标画直方图的话,它的形状就是一个金字塔型。我们绑定物品同时选取,其实就相当于绑定一行,每行都是整块选取。
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=2005;
const int maxm=505;
typedef long long ll;
int n,m,k;
ll dp[maxm];
ll tw[maxn],tv[maxn];
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
cin>>tw[i]>>tv[i];
tw[i]+=tw[i-1];
tv[i]+=tv[i-1];
}
for(int l=1;l<=k;l++)
for(int r=k;r<=n;r++){
ll w=tw[r]-tw[l-1],v=tv[r]-tv[l-1];
for(int j=w;j<=m;j++)
dp[j]=max(dp[j],dp[j-w]+v);
}
for(int i=1;i<=m;i++){
dp[i]=max(dp[i-1],dp[i]);
cout<<dp[i]<<" ";
}
return 0;
}
D.智乃想考一道完全背包(Hard version)
分析:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005, MAXM = 505;
long long w[MAXN], v[MAXN], dp[MAXN][MAXM], ans[MAXN][2];
int main()
{
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i)
{
scanf("%lld %lld", &w[i], &v[i]);
}
for (int i = 1; i <= n; ++i)
{
w[i] += w[i - 1];
v[i] += v[i - 1];
ans[i][1] = 1;
}
// 整体枚举思路
// 固定i,枚举区间[i,n], [i,n-1], ... [i,i]
// 我们发现,因为是从后往前枚举,确保最后 取的数量 a[i]>=a[i+1]>=...>=a[n]
// 其次,i从后往前递增,
// 我们发现,此时保证了当下一个递增顶点从i变成了i+1时,保证a[i]>=a[i-1]
// 通过外部、内部两层循环,优雅的实现了a[1]<=a[2]<=...a[K]>=a[K+1]>=...>=a[n]
for (auto i = 1; i <= n; ++i) // 枚举从i做为 取值顶点时的最大价值
{
for (auto j = n; j >= i; --j) // 从后往前枚举
{
// 这里w[j]-w[i-1]表示 是否取[i,j]的区间元素,且元素是无限个
long long W = w[j] - w[i - 1];
long long V = v[j] - v[i - 1];
for (auto k = W; k <= m; ++k) // 完全背包,所以从前往后枚举
{
dp[j][k] = max({dp[j][k], dp[j + 1][k], dp[j][k - W] + V});
}
}
for (auto j = 0; j <= m; ++j)
{
if (dp[i][j] > ans[j][0]) // 更新容量为j的最优值
{
ans[j][0] = dp[i][j];
ans[j][1] = i;
}
}
}
for (auto i = 1; i <= m; ++i)
{
printf("%lld%c", ans[i][0], " \n"[i == m]);
}
for (auto i = 1; i <= m; ++i)
{
printf("%lld%c", ans[i][1], " \n"[i == m]);
}
return 0;
}