https://vjudge.net/contest/147628
password: acmlab2016
A - A HDU2037
【题意】
给你一组电视节目的开始结束时间,让你尽可能多的去看电视.
【思路】
贪心.
节目结束时间相同时,肯定选择更晚开始的更优.明白了这一点剩下的就是排序+选取了.
【Code】
#include <cstdio>
#include <algorithm>
using namespace std;
struct node
{
int t1;
int t2;
}a[105];
int cmp(node a,node b)
{
if(a.t2==b.t2) return a.t1>b.t1;
return a.t2<b.t2;
}
int main()
{
int n,i,j,k,t;
while(scanf("%d",&n)&&n)
{
for(i=0;i<n;i++) scanf("%d %d",&a[i].t1,&a[i].t2);
sort(a,a+n,cmp);
for(i=1,t=a[0].t2,k=1;i<n;i++)
{
if(a[i].t1>=t)
{
t=a[i].t2;
k++;
}
}
printf("%d\n",k);
}
return 0;
}
B - B HDU1051
【题意】
给你一堆木棍,开始安装木棍有个setup time,如果下一个安装的木棍的l和w均小于现在这个的木棍,安装下一个木棍就不需要setup time.让你求最小的setup time.
【思路】
贪心.
先从l和w都比较大的那根木棍开始装,装完这个去装比他l和w都小一点的木棍,再装更小的…..
【Code】
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
struct node
{
int l,w;
} a[5050];
bool cmp(node x,node y)
{
if (x.l>y.l) return 1;
else if (x.l==y.l) return x.w>y.w;
return 0;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for (int i=0;i<n;i++) scanf("%d %d",&a[i].l,&a[i].w);
sort(a,a+n,cmp);
//for (int i=0;i<n;i++) printf("%d %d\n",a[i].l,a[i].w);
int tot=n;
for (int i=1;i<n;i++)
for (int j=0;j<i;j++)
if (a[j].l>=a[i].l&&a[j].w>=a[i].w){
tot--;
a[j].l=a[i].l;
a[j].w=a[i].w;
a[i].l=0;
a[i].w=0;
break;
}
printf("%d\n",tot);
}
return 0;
}
C - C HDU2141
【题意】
给你a,b,c三个序列,和一组数据x问是否存在Ai+Bj+Ck=X.
【思路】
一开始直接O(T*l*n*logm*s)的做法直接TLE了…(l,n,m,s分别是a,b,c,x序列的元素个数)
仔细想了想,l,n,m<=500,那么完全可以先生成a,b(其他亦可)两个序列的任意两个数的和,再在这个序列中去二分查找x-c[i].
时间复杂度降到了O(T*log(l*n)*m*s)
【Code】
#include<cstdio>
#include<algorithm>
using namespace std;
int l,n,m,x,tmp;
int a[505],b[505],c[505],ab[250010];
int main()
{
int T=0;
while (~scanf("%d %d %d",&l,&n,&m))
{
T++;
for (int i=0;i<l;i++) scanf("%d",&a[i]);
for (int i=0;i<n;i++) scanf("%d",&b[i]);
for (int i=0;i<m;i++) scanf("%d",&c[i]);
int tot=0;
for (int i=0;i<l;i++)
for (int j=0;j<n;j++)
ab[tot++]=a[i]+b[j];
sort(ab,ab+tot);
scanf("%d",&x);
printf("Case %d:\n",T);
while (x--)
{
scanf("%d",&tmp);
bool flag=false;
for (int i=0;i<m;i++)
{
int opt=tmp-c[i];
int l=0,r=tot-1,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (ab[mid]==opt){
flag=true;
break;
}
if (ab[mid]<opt) l=mid+1;
else r=mid-1;
}
if (flag) break;
}
if (flag) printf("YES\n");
else printf("NO\n");
}
}
}
D - D HDU4004
【题意】
给一个[0,L]的小河中间有n块石头,最多跳m次,求青蛙跳跃能力(单次所跳最大距离)至少为多少.
【思路】
最大值最小问题或最小值最大的问题,一般就是二分.
青蛙的跳跃能力越强需要的次数就越少,因此青蛙的跳跃能力有单调性,所以二分枚举青蛙的跳跃能力,在判断这个能力下是否能够跳过去,每次在这个能力下尽可能跳到远的石头上,最后看m次是否能跳到对岸。
【Code】
#include<cstdio>
#include<algorithm>
using namespace std;
int L,n,m;
int a[500050];
int ok(int mid)
{
int cnt=1,tot=0;
for (int i=1;i<=n;i++)
{
tot+=a[i];
if (tot>mid) {
tot=a[i];
cnt++;
}
}
if (cnt<=m&&tot<=mid) return true;
return false;
}
int main()
{
while(~scanf("%d %d %d",&L,&n,&m))
{
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
a[++n]=L;a[0]=0;
sort(a,a+n);
int l=0,r=L,mid;
for (int i=n;i>=1;i--)
{
a[i]-=a[i-1];
l=max(l,a[i]);
}
while (l<r)
{
mid=(l+r)/2;
if (ok(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
}
E - E POJ3617
【题意】
n头奶牛,每个奶牛用一个大写字母代表.给你一个初始序列,每次只能从头或尾取字母,求字典序最小的一种取的方案.
【思路】
贪心.每次取都判断头或尾是否相同,不相同取字典序最小的,相同则判断他们相邻的下一个字母是否相同…依次类推
/* 其实可以存成两个字符串,每次取的时候从这边的头取,从那边的尾也取一个,然后判断是否相同直接比较字符串就行 */
【Code】
#include <iostream>
#include<cstdio>
#include <string>
using namespace std;
int n;
char s[2010];
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)cin>>s[i];
int l=0,r=n-1,cnt=0;
while(l<=r)
{
bool flag=false;
for(int i=0;i<=r-l; i++)
{
if (s[l+i]<s[r-i])
{
flag=true;
cnt++;
break;
}else if (s[l+i]>s[r-i])
{
flag=false;
cnt++;
break;
}
}
if(flag) printf("%c",s[l++]);
else printf("%c",s[r--]);
if(cnt%80==0) printf("\n");
}
printf("\n");
return 0;
}
F - F POJ3273
【题意】
给你n个数据,让你把他分为m份,使每一份和的最大值最小.
【思路】
二分每份和的最大值,看是否能分成至多m份(显然能分成m份一定能分成m+1份m+1<=n)
【Code】
#include<cstdio>
int n,m;
int d[100010];
bool ok(int x)
{
int tot=0,num=1;
for (int i=0;i<n;i++)
{
if (tot+d[i]<=x)
{
tot+=d[i];
}else{
tot=d[i];
num++;
}
}
if (num<=m) return true;
return false;
}
int main()
{
int l=0,r=0,mid;
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++)
{
scanf("%d",&d[i]);
if (d[i]>l) l=d[i];
r+=d[i];
}
while (l<=r)
{
mid=(l+r)/2;
if (ok(mid)) r=mid-1;
else l=mid+1;
}
printf("%d\n",mid);
return 0;
}
G - G POJ3111
【题意】
本题即让我们从n件物品中选出k个使这个式子值最大.
【思路】
容易想到去二分s,看是否能找到符合的物品.但具体怎么做一开始还是没思路…看了题解知道使用01分数规划(但我之前也不知道这个东西).
下面介绍一下:
http://blog.csdn.net/hhaile/article/details/8883652
01分数规划问题:所谓的01分数规划问题就是指这样的一类问题,给定两个数组,a[i]表示选取i的收益,b[i]表示选取i的代价。如果选取i,定义x[i]=1否则x[i]=0。每一个物品只有选或者不选两种方案,求一个选择方案使得R=sigma(a[i]*x[i])/sigma(b[i]*x[i])取得最值,即所有选择物品的总收益/总代价的值最大或是最小。
数学分析中一个很重要的方法就是分析目标式,这样我们来看目标式。
R=sigma(a[i]*x[i])/sigma(b[i]*x[i])
我们来分析一下他有什么性质可以给我们使用。
我们先定义一个函数F(L):=sigma(a[i]*x[i])-L*sigma(b[i]*x[i]),显然这只是对目标式的一个简单的变形。分离参数,得到F(L):=sigma((a[i]-L*b[i])*x[i])。这时我们就会发现,如果L已知的话,a[i]-L*b[i]就是已知的,当然x[i]是未知的。记d[i]=a[i]-L*b[i],那么F(L):=sigma(d[i]*x[i]),多么简洁的式子。我们就对这些东西下手了。
再次提醒一下,我们的目标是使R取到最大值。
我们来分析一下这个函数,它与目标式的关系非常的密切,L就是目标式中的R,最大化R也就是最大化L。
F的值是由两个变量共同决定的,即方案X和参数L。对于一个确定的参数L来说,方案的不同会导致对应的F值的不同,那么这些东西对我们有什么用呢?
假设我们已知在存在一个方案X使得F(L)>0,这能够证明什么?
F(L)=sigma(a[i]*x[i])-L*sigma(b[i]*x[i])>0即sigma(a[i]*x[i])/sigma(b[i]*x[i])>L也就是说,如果一个方案使得F(L)>0说明了这组方案可以得到一个比现在的L更优的一个L,既然有一个更优的解,那么为什么不用呢?
显然,d数组是随着L的增大而单调减的。也就是说,存在一个临界的L使得不存在一种方案,能够使F(L)>0. 我们猜想,这个时候的L就是我们要求的最优解。之后更大的L值则会造成无论任何一种方案,都会使F(L)<0.类似于上面的那个变形,我们知道,F(L)<0是没有意义的,因为这时候的L是不能够被取得的。当F(L)=0使,对应方案的R值恰好等于此时的L值。
根据这样的一段性质,很自然的就可以想到二分L值,然后验证是否存在一组解使得F(L)>0,有就移动下界,没有就移动上界。
上面的内容理解了,这道题就应该没什么问题了.
【Code】
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const double eps=1.0E-6;
int n,k;
struct node{
int v,w;
}a[100010];
struct ppt{
int id;
double x;
}b[100010];
bool cmp(const ppt &u,const ppt &v)
{
return u.x>v.x;
}
bool ok(double s)
{
for (int i=0;i<n;i++)
{
b[i].id=i+1;
b[i].x=a[i].v-s*a[i].w;
}
sort(b,b+n,cmp);
double sum=0.0;
for (int i=0;i<k;i++)
{
sum+=b[i].x;
}
return sum>=0.0;
}
int main()
{
scanf("%d %d",&n,&k);
for (int i=0;i<n;i++)
{
scanf("%d %d",&a[i].v,&a[i].w);
}
double l=0.0,r=0x3f3f3f3f,mid;
while (r-l>eps)
{
mid=(l+r)/2;
if (ok(mid)) l=mid;
else r=mid;
}
for (int i=0;i<k-1;i++)
printf("%d ",b[i].id);
printf("%d\n",b[k-1].id);
return 0;
}