题目链接
cf 344e
题意:
有n个磁头,m个位置需要访问,磁头每一秒移动一格,求访问m个磁头需要的最少时间。
思路:
首先老规矩,求最少最多的问题,立马想到二分,对于这个题目,很明显二分时间带入验证可行.
那么问题就转化成了怎么验证?
要想在一定时间内访问多的磁道,那么基于贪心的思想,如果有磁道在当前磁头的左边,那么
我们应该在读完最左边的磁道之后应该让当前磁头尽可能往右走去读更多的磁道,以减少其
他磁头的访问的磁道数从而减少访问时间.
那么存在以下两种情况:
1.当前磁头的左面没有磁道要读,那么当前磁头要尽可能往右走去访问更多磁道。
2.反之,那么该磁头也有两种走法,我们设磁头和磁道距离为x,往右走的距离为y,二分时间为s
(1)先往左走再往右走,那么y=s-2x
(2)先忘右走再往左走,那么y=(s-x)/2.
然后每次都要维护这两个y的最大值,然后check即可.
#include
#define Ri(a) scanf("%d", &a)
#define Rl(a) scanf("%lld", &a)
#define Rf(a) scanf("%lf", &a)
#define Rs(a) scanf("%s", a)
#define Pi(a) printf("%d\n", (a))
#define Pf(a) printf("%lf\n", (a))
#define Pl(a) printf("%lld\n", (a))
#define Ps(a) printf("%s\n", (a))
#define W(a) while(a--)
#define CLR(a, b) memset(a, (b), sizeof(a))
#define MOD 1000000007
#define inf 0x3f3f3f3f
#define exp 0.00000001
#define pii pair
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int n,m;
ll h[maxn],p[maxn];
int check(ll x)
{
int item=1;
ll s=0;
for(int i=1;i<=n&&item<=m;i++)
{
if(h[i]-p[item]>x)
return 0;
s=h[i];
if(h[i]
cf 830a
题意:
给你n个人和k把钥匙分别在某个位置,然后每个钥匙只能被一个人拥有,且都能打开最后的门p,
问你所有人都到达终点所用的最短时间。
思路:
首先,这个题目和上面的题目有很多相似之处,
1.都是求最小时间,那么所有人都在我们二分的时间之内能走出去即可
2.上面那个题目要求必须所有的磁道都读完,所以有一个不满足这个时间就不可以,而这个
题目要求人能走出去,只要能在时间内找到钥匙并走出去即可.
3.验证还是要贪心.将所有钥匙都排序,所有的人能尽量选择左边的满足时间的钥匙,这样才能减少对后
面的人的影响,因为如果往右选的话,有可能他选的正好是最后一把钥匙那么右面的人就没得选了.
二分+贪心
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=5e3+10;
int n,k;
ll a[maxn],p, b[maxn];
bool check(ll x)
{
int item=1;
for(int i=1;i<=n;i++)
{
while(item<=k&&(abs(a[i]-b[item])+abs(b[item]-p))>x)
item++;
if(item>k)
return false;
item++;
}
return true;
}
int main(){
scanf("%d %d %lld",&n,&k,&p);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=k;i++)
scanf("%lld",&b[i]);
sort(a+1,a+1+n);
sort(b+1,b+1+k);
ll l=0,r=LONG_LONG_MAX,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
{
r=mid-1;
}
else
l=mid+1;
}
printf("%lld\n",l);
return 0;
}
这个题目dp也可以做.常规的二维dp.
dp[i][j]表示前i个人拿前j把钥匙走出去的最少时间,
dp[i][j]=min(dp[i][j-1],max(dp[i-1][j-1],abs(a[i]-b[j])+abs(p-b[j])))
//常规问题,第j把钥匙取与不取的问题.
#include
#define inf 0x7f3f3f3f
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=5e3+10;
int n,k;
ll p;
ll dp[maxn][maxn],a[maxn],b[maxn];
int main(){
scanf("%d %d %lld",&n,&k,&p);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=k;i++)
scanf("%lld",&b[i]);
sort(a+1,a+1+n);
sort(b+1,b+1+k);
memset(dp,inf,sizeof(dp));
for(int i=0;i<=k;i++)
dp[0][i]=0;
for(int i=1;i<=n;i++)
{
for(int j=i;j<=k;j++)
{
dp[i][j]=min(dp[i][j-1],max(dp[i-1][j-1],abs(a[i]-b[j])+abs(p-b[j])));
}
}
printf("%lld\n",dp[n][k]);
return 0;
}
另外一种做法是,直接贪心.根据贪心可以得到结论,所选的钥匙必为连续的n把钥匙,
才能使得所花费的时间最短.
可以这样去想,因为n个人拿完钥匙之后会要去往门p,所以他们在拿钥匙的过程中,
根据贪心的思维肯定是尽可能往门p的方向走,如果不够的话再往反方向去走.
如果往p的方向钥匙够了,其实时间就是他们到p的时间.
如果不够,那么往反方向走也肯定会选距离最近的那个钥匙去减少时间花费.
#include
#define Ri(a) scanf("%d", &a)
#define Rl(a) scanf("%lld", &a)
#define Rf(a) scanf("%lf", &a)
#define Rs(a) scanf("%s", a)
#define Pi(a) printf("%d\n", (a))
#define Pf(a) printf("%lf\n", (a))
#define Pl(a) printf("%lld\n", (a))
#define Ps(a) printf("%s\n", (a))
#define W(a) while(a--)
#define CLR(a, b) memset(a, (b), sizeof(a))
#define MOD 1000000007
#define inf 0x3f3f3f3f
#define exp 0.00000001
#define pii pair
#define mp make_pair
#define pb push_back
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
ll n,k,p;
ll a[2333],b[2333];
int main()
{
Rl(n),Rl(k),Rl(p);
for(int i=1;i<=n;i++) Rl(a[i]);
for(int i=1;i<=k;i++) Rl(b[i]);
ll ans=INT_MAX,tmp;
sort(a+1,a+1+n);
sort(b+1,b+1+k);
for(int i=1;i<=k-n+1;i++)
{
tmp=0;
int x,y;
for(x=1,y=i;x<=n;x++,y++)
{
tmp=max(tmp,abs(b[y]-a[x])+abs(b[y]-p));
}
ans=min(ans,tmp);
}
Pl(ans);
return 0;
}