P1233 木棍加工
https://www.luogu.org/problem/P1233
问题可以理解成一个矩形的覆盖问题,木棍的长与宽就是矩形的长与宽,如果一个矩形能够完全覆盖另一矩形(即一个木棍的长和宽都大于等于另一木棍)则处理下一木棍就不需要准备时间,这样就实现了本题的目标,即让加工木棍的准备时间最短。
那么,怎样同时保证木棍的两个属性都具有单调性呢? 显然要先对一个属性排序sort大法好
接下来另一组属性的单调递减只需要求出不下降子序列的个数即可,因为从一个子序列转换到另一子序列只需一分钟的准备时间,所以不下降子序列的个数就是总共的准备时间。
从dilworth定理中,我们可知,下降子序列的最小划分等于最长不下降子序列的长度
于是题目转换为一道基本的dp求最长不下降子序列的题目
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct node
{
int L;
int W;
}a[5005];
int b[5005];
bool cmp(node x,node y)
{
return x.L>y.L;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].L,&a[i].W);
}
sort(a+1,a+1+n,cmp);
int ans=0;
//下降子序列的最小划分等于最长不下降子序列的长度
for(int i=1;i<=n;i++)
{
if(a[i].W>b[ans])
{
b[ans+1]=a[i].W;
ans++;
}
else
{
int l=1,r=ans;//这里相当于在当前b数组里面寻找a[i].w适合存的位子
while(l<=r)
{
int mid=(l+r)/2;
if(b[mid]>=a[i].W)
r=mid-1;
else
l=mid+1;
}
b[l]=a[i].W;
}
}
cout<<ans<<endl;
return 0;
}
P1316 丢瓶盖
https://www.luogu.org/problemnew/show/P1316
方法就是二分答案。
注意在输入完坐标时,排序。(刚开始还疑惑要是没连着怎么办,我怎么这么蠢)
代码注释可以很好理解这道题的解法
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int A,B;
ll ans;
ll a[100005];
bool check(ll dd)
{
ll tot=1;
ll num=1;
for(int i=2;i<=A;i++)
{
if(a[i]-a[num]>=dd)
{
tot++;//我们当前想的是最大距离为dd,当大于等于了,就应该选一个点了
num=i;//代表上一个被选择的点,作记录
}
}
if(tot<B)//如果选的数还不足B,说明距离偏大,不符合要求
{
return false;
}
else//否则正确
return true;
}
int main()
{
scanf("%d%d",&A,&B);
for(int i=1; i<=A; i++)
{
scanf("%lld",&a[i]);
}
sort(a+1,a+1+A);
ll l=0,r=a[A]-a[1],mid;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
cout<<ans<<endl;
return 0;
}
P1182 数列分段 Section II
https://www.luogu.org/problemnew/show/P1182
//注意l不能从0开始,数字是不可以分开的,可能没有为0的数字,
//所以要从最大的数开始,保证数字没有拆分
//注意l不能从0开始,数字是不可以分开的,可能没有为0的数字,
//所以要从最大的数开始,保证数字没有拆分
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100100],n,m;
bool check(int x)
{
int sum = 0, cnt = 1;
for (int i=1; i<=n; ++i)
{
sum += a[i];
if (sum>x)
{
cnt++;
sum = a[i];
if (cnt>m) return false ;
}
}
return true ;
}
int main()
{
int l = 0, r = 0, ans;
scanf("%d%d",&n,&m);
for (int i=1; i<=n; ++i)
scanf("%d",&a[i]), r += a[i], l = max(l,a[i]);
while (l<=r) //l不能从0开始,
{
int mid = (l+r)>>1;
if (check(mid))//符合要求
{
ans = mid;//即可以更新值
r = mid-1;//但我们需要的是分段和最大值的最小值,所以让r减小
}
else l = mid+1;
}
printf("%d",ans);
return 0;
}
P1057 传球游戏
https://www.luogu.org/problemnew/show/P1057
做题的时候不要去想怎么数怎么数,不要想多么复杂,这样会做不出来题的。
直接从题意思考,
设a[i][k]代表经过k次传到了i的方案数
a[1][0]=1;//没有传 到本身的方案数当然是1咯
//我们需要的答案是a[1][m]
//不要把问题想复杂,这就是dp呀
//传到i号同学的球只能来自于i的左边和右边
//所以a[i][k]=a[i-1][k-1]+a[i+1][k-1]
//然后对于1,n的情况要单独处理,a[1][k]=a[2][k-1]+a[n][k-1]
//a[n][k]=a[n-1][k-1]+a[1][k-1]
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[35][35];
//a[i][k]代表经过k次传到了i的方案数
int main()
{
scanf("%d%d",&n,&m);
a[1][0]=1;//没有传 到本身的方案数当然是1咯
//我们需要的答案是a[1][m]
//不要把问题想复杂,这就是dp呀
//传到i号同学的球只能来自于i的左边和右边
//所以a[i][k]=a[i-1][k-1]+a[i+1][k-1]
//然后对于1,n的情况要单独处理,a[1][k]=a[2][k-1]+a[n][k-1]
//a[n][k]=a[n-1][k-1]+a[1][k-1]
for(int k=1;k<=m;k++)
{
for(int j=2;j<n;j++)
{
a[j][k]=a[j-1][k-1]+a[j+1][k-1];
}
a[1][k]=a[2][k-1]+a[n][k-1];
a[n][k]=a[n-1][k-1]+a[1][k-1];
}
cout<<a[1][m]<<endl;
return 0;
}
P1192 台阶问题
https://www.luogu.org/problemnew/show/P1192
找规律太重要了,思考能力也比较重要啊。
可以自己模拟一下,可以发现每一个点的递推关系为s[n]=求和(s[n-k]~s[n-1])
就可以想象为最后一步:越1~k个台阶的情况,相加起来
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int mod=100003;
int s[100005];
int n,k;//最高n层,最多一次k步,求方案数
//可以自己模拟一下,可以发现每一个点的递推关系为s[n]=求和(s[n-k]~s[n-1])
//就可以想象为最后一步:越1~k个台阶的情况,相加起来
int main()
{
scanf("%d%d",&n,&k);
s[0]=1;//初始化,一种情况
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k&&(i-j)>=0;j++)
{
s[i]=(s[i]+s[i-j])%mod;
}
}
cout<<s[n]<<endl;
return 0;
}