浅谈贪心【复习】
NYG的背包
【问题描述】
NYG有一个神奇的背包,每放进去一个物品,背包的体积就会变大。
也就是说,每放进一个物品,背包会被占用一定的体积,但是紧接着背包的总体积又会增大一定的值(注意是在放入物品后背包总体积才增大)。
NYG发觉这个背包十分好用,于是不由自主地想到了一个问题。
现在给出背包初始容量V 以及n个物品,每一个物品两个值a, b,分别表示物品所占体积 和放入背包后背包增大的体积。
NYG想知道能否把所有物品装进去? 因为NYG比较老实,这么简单的问题自然装作不会做的样子。 于是他来请教你。
分析:
因为只问你能否装完,这就好办了多
肯定需要把a<b的先用了,才可能搞得定a>b的
如果前者都没法的话,更就别说后者
对于a<b的部分,肯定是先用a较小的
因为有可能只有把前几个空间赚回来了才能装下后面几个
关键就在于a>b的情况
这时发现肯定是亏空间的,考虑最优一定是先亏小空间,这样能腾出更多的空间为后面放
读完题别以为是背包....
code:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 100004;
inline int read()
{
int ret, f=1;
char c;
while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
ret=c-'0';
while((c=getchar())&&(c>='0'&&c<='9'))ret = (ret<<3)+(ret<<1)+c-'0';
return ret*f;
}
int T, n, s, cnt1, cnt2;
struct thi{
ll a, b;
}t[maxn], f[maxn];
bool cmp1(thi x, thi y)
{
return x.a < y.a;
}
bool cmp2(thi x, thi y)
{
return x.b > y.b;
}
int main()
{
T = read();
while(T--)
{
n = read();s = read();
cnt1 = cnt2 = 0;
for(int i = 1; i <= n; ++i)
{
ll x = 1LL * read(), y = 1LL * read();
if(y > x)
t[++cnt1] = (thi){x, y};
else
f[++cnt2] = (thi){x, y};
}
sort(t + 1, t + cnt1 + 1, cmp1);
ll now = 1LL * s;
bool fl = 0;
for(int i = 1; i <= cnt1; ++i)
{
now -= t[i].a;
if(now < 0)
{
fl = 1;
break;
}
now += t[i].b;
}
if(!fl)
{
sort(f + 1, f + cnt2 + 1, cmp2);
for(int i = 1; i <= cnt2; ++i)
{
now -= f[i].a;
if(now < 0)
{
fl = 1;
break;
}
now += f[i].b;
}
}
printf("%s\n", fl? "No": "Yes");
}
return 0;
}
描述
一张普通的国际象棋棋盘,它被分成 8 乘 8 (8 行 8 列) 的 64 个方格。设有形状一样的多米诺牌,每张牌恰好覆盖棋盘上相邻的两个方格,即一张多米诺牌是一张 1 行 2 列或者 2 行 1 列的牌。那么,是否能够把 32 张多米诺牌摆放到棋盘上,使得任何两张多米诺牌均不重叠,每张多米诺牌覆盖两个方格,并且棋盘上所有的方格都被覆盖住?我们把这样一种排列称为棋盘被多米诺牌完美覆盖。这是一个简单的排列问题,同学们能够很快构造出许多不同的完美覆盖。但是,计算不同的完美覆盖的总数就不是一件容易的事情了。不过,同学们 发挥自己的聪明才智,还是有可能做到的。
现在我们通过计算机编程对 3 乘 n 棋盘的不同的完美覆盖的总数进行计算。
分析:
首先奇数肯定无解,所以考虑偶数递推转移
很明显设f[i]表示前i列的方案数
只看两列的话只有三种方法
所以有f[i]=f[i-2]*3
但是有一种四列格子的会多出两种方案
一种六列格子会多出两种方案
所以转移就有
f(n)=3f(n-2)+2f(n-4)+2f(n-6)+...+2f(0)
则
f(n-2)= 3f(n-4)+2f(n-6)+......+2f(0)
两式相减得到
f(i)=4f(i-2)-f(i-4)
递推转移就行
代码就是一个递推式,就懒得写了
分析:
排完序后
答案选的区间一定是一个连续的区间
一个连续的区间什么时候取到最优解呢?
当然是中位数了
这样用尺取法双指针就可以做到O(n)判断了
code by std:
#include<cstdio>
#include<algorithm>
using namespace std;
#define rep(i,n) for(int i=1;i<=n;++i)
#define mp make_pair
#define pb push_back
#define x0 gtmsub
#define y0 gtmshb
#define x1 gtmjtjl
#define y1 gtmsf
long long s[100010];
int n,k,a[100010],ans;
bool check(int l,int r)
{
int x=(l+r)>>1;
return (1ll*(x-l+1)*a[x]-(s[x]-s[l-1]))+((s[r]-s[x])-1ll*(r-x)*a[x])<=k;
}
int main()
{
freopen("signin.in", "r", stdin);
freopen("signin.out", "w", stdout);
scanf("%d%d",&n,&k);
rep(i,n)scanf("%d",&a[i]);
sort(a+1,a+n+1);
rep(i,n)s[i]=s[i-1]+a[i];
int now=1;
rep(i,n)
{
now=max(now,i);
for(;now<=n&&check(i,now);now++);
ans=max(ans,now-i);
}
printf("%d\n",ans);
return 0;
}
这题果然够签到的
吐槽:最讨厌这种n又要考虑点,又要考虑边的题了
假如说考虑将点x设为晴天点
把该点之前的点和该点之后的点的贡献分别考虑
将原式化简
code by std:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#define N 1000005
#define ll long long
using namespace std;
int n,d;
int s[N];
ll sum1[N],sum2[N],sum3[N],ans[N];
ll work(int a,int b,int x){
ll s1,s2,s3;
if(a<=0||a>n) s1=sum1[b],s2=sum2[b],s3=sum3[b];
else s1=sum1[b]-sum1[a],s2=sum2[b]-sum2[a],s3=sum3[b]-sum3[a];
ll an=s3+(ll)2*s2*x+(ll)x*x*s1;
return an;
}
int main()
{
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
for(int i=1;i<=n;i++) sum1[i]=sum1[i-1]+s[i],sum2[i]=sum2[i-1]+(ll)i*s[i],sum3[i]=sum3[i-1]+(ll)i*i*s[i];
for(int i=1;i<=n+1;i++) ans[i]+=work(i-d-1,i-1,d-i+1);
for(int i=n;i>=1;i--) sum1[i]=sum1[i+1]+s[i],sum2[i]=sum2[i+1]+(ll)(n-i+1)*s[i],sum3[i]=sum3[i+1]+(ll)(n-i+1)*(n-i+1)*s[i];
for(int i=1;i<=n+1;i++) ans[i]+=work(i+d,i,d+i-n-1);
ll mx=0;
for(int i=1;i<=n+1;i++) mx=max(mx,ans[i]);
printf("%lld\n",mx);
return 0;
}
https://www.luogu.org/problem/P4393
题目描述
对于一个给定的序列a1, …, an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai和ai+1用一个元素max(ai,ai+1)替代,这样得到一个比原来序列短的新序列。这一操作的代价是max(ai,ai+1)。进行n-1次该操作后,可以得到一个长度为1的序列。
我们的任务是计算代价最小的reduce操作步骤,将给定的序列变成长度为1的序列。
分析:
事实上每个数最多只可能被有效加两次(想一想,为什么)
合并一次后被另一个数合并,或者两边的数都比他小(如图a3)
而看每一边的情况
当一边的数都比a[i]小,最终一定会使a[i]被加一次,如果如图中a3,一侧有a1比a3大,显然在加上a1前会先和a3合并使得结果更优
另一边同理
code:
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n;
long long ans, a[1000100];
int main()
{
cin >> n;
for (int i = 0; i < n; i++){
cin >> a[i];
}
for (int i = 1; i < n; i++){
ans += max(a[i - 1], a[i]);
}
cout << ans;
}