/*
题意:给出n条线段,以米的单位给出,小数点后两位(精确到厘米),要你对这些线段裁剪,裁剪出m条等长的线段,并且让这些线段尽可能长另外线段的长度不能小于1厘米,如果筹不够m条,输出0.00
POJ 1064
*/
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10010;
double num[maxn];
double eps=1e-5;
int main(){
int n,k;
while(~scanf("%d%d",&n,&k)){
double maxvalue=0;
for(int i=0;i<n;i++){
scanf("%lf",num+i);
if(num[i]>maxvalue)
maxvalue=num[i];
}
double lp=0,rp=maxvalue;
while(rp-lp>eps){
double mid=(rp+lp)/2;
int sum=0;
for(int i=0;i<n;i++){
sum+=num[i]/mid;
}
if(sum>=k)
lp=mid;
else
rp=mid;
}
printf("%0.2lf\n",int(rp*100)*0.01);
}
}
//用整数来二分
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10010
#define MAX 0x3f3f3f3f
int a[N];
int main(){
int n,m;
double len;
while(~scanf("%d%d",&n,&m)){
int Max=0;
for(int i=0;i<n;i++){
scanf("%lf",&len);
a[i]=len*100;
Max=max(Max,a[i]);
}
int low=1,high=Max;
int res=0;
while(low<=high){
int mid=(low+high)>>1;
int sum=0;
for(int i=0;i<n;i++)
sum+=a[i]/mid;
if(sum>=m)
res=max(res,mid),low=mid+1;
else
high=mid-1;
}
printf("%.2lf\n",(double)res/100.0);
}
}
/*
一条线段上有 n 个点,选取 m 个点,使得相邻点之间的最小距离值最大。
POJ 2456
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int L,n,m,pos[100005];
bool can(int l){
int cnt=1,cur=pos[0];
for(int i=1;i<n;i++){
while(i<n&&pos[i]-cur<l)
i++;
cur=pos[i];
if(i<n&&++cnt==m)
return true;
}
return false;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n;i++)
scanf("%d",&pos[i]);
sort(pos,pos+n);
int l=1,r=pos[n-1]-pos[0];
while(l<=r){
int mid=(l+r)/2;
if(can(mid))
l=mid+1;
else
r=mid-1;
}
printf("%d\n",r);
}
}
这道题目是一道0-1分数规划求最优值。
方法是一个二分搜索+贪心的题目。
出这道题目就是告诉大家二分不仅可以查找,还可以搜索一个更优值。
要使得单位重量的价值最大,则其最大不超过单个中最大的单位重量的价值,最小当然不小于0.
那么我们就这一在0--最大单位重量的价值中间找一个值ans,使得ans为满足题目条件的最大值。如果满足条件,则可以找更大的。设置一个条件。既二分搜索、
从n个物品中找k个使得k个的价值和/质量和>=ans
为了使得ans尽可能的大,那么这里就要贪心选择。
#include<cstdio>
#include<algorithm>
using namespace std;
double wi[10005],vi[10005],pi[10005];
int n,k;
bool cmp(double a,double b){
return a>b;
}
int check(double x){
for(int i=0;i<n;i++){//p[i] 代表的是 自己本省的价值-物品重量*最高单价
pi[i]=vi[i]-wi[i]*x;
}
double y=0;
sort(pi,pi+n,cmp); //p[i] 由大到小排序。
for(int i=0;i<k;i++){
y+=pi[i];
}
return y>=0;
}
double reach (double m){// 二分搜索
double l=0,r=m,mid;
for(int i=0;i<100;i++){
mid=(r+l)/2;
if(check(mid))
l=mid;
else
r=mid;
}
return l;
}
int main(){
while(~scanf("%d%d",&n,&k)){
double ma=0;
for(int i=0;i<n;i++){
scanf("%lf%lf",&wi[i],&vi[i]);
double ant=vi[i]/wi[i];
if(ant>ma)
ma=ant;//选择出单价最高的。
}
printf("%0.2lf\n",reach(ma));
}
}
/*
POJ 3104
【题意】:给出n件刚洗完的衣服,每件衣服有一个属性ai,表示改衣服在自然风干的条件需要ti分钟。现在有一台烘干机,每分钟你可以选择把任意一件衣服放进去,那么这件衣服风干的时间就减少k分钟,而每分钟对于不在烘干机的衣服,风干时间都减少1。问最少用多少时间使所有衣服都干了。
【题解】:直接二分答案,然后判断答案的正确性。
假设当前二分的答案为 t,那么:
对于ai <= t的衣服,显然让它们自然风干就可以了。
对于ai > t的衣服,我们需要知道该衣服最少用多少次烘干机。
设该衣服用了x1分钟风干,用了x2分钟烘干机。
那么有 x1 + x2 = t 和 ai <= x1 + x2 * k,联立两式可得 x2 >= (ai - t) / (k - 1),即最少使用次数为[(ai - t) / (k - 1)] 的最小上界。
************************************************
特别注意 k不能等于1,k等于分母就等于0了
****************************************************
最后,判断一下总使用次数是否少于 t 即可。
*/
#include<cstdio>
#include<cstring>
#define N 100005
using namespace std;
int time[N];
int n,k;
bool check(int t){
int cnt=0;
for(int i=0;i<n;i++){
if(time[i]<t) continue; //自然风干的时间比其要短,那就让它自然风干
double temp=(double)(time[i]-t)/(k-1);//保证一个物体占用甩干机的时间最短,即为(time[i]-t)/(k-1)
cnt+=(int)temp;
if(temp-(int)temp>0)
++cnt;//向上取整。如果temp为3.4 ,那么cnt就应该为4
if(cnt>t)
return false;
}
return true;
}
int main(){
int low,high,mid,ans;
while(~scanf("%d",&n)){
high=0;low=0;
for(int i=0;i<n;i++){
scanf("%d",&time[i]);
if(time[i]>high)
high=time[i];
}
scanf("%d",&k);
if(k==1){ //k不能为1,为1直接输出
printf("%d\n",high);
continue;
}
ans=high;
while(low<=high){
mid=low+(high-low)*0.5;
if(check(mid)){
ans=mid;
high=mid-1;
}
else
low=mid+1;
}
printf("%d\n",ans);
}
}
/*
求一个有n个正整数组成的序列,给定整数S,求长度最短的连续序列,使得它们的和大于等于S
POJ 3061
*/
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 100005
int n;
int a[maxn],sum[maxn],s;
bool check(int x){
for(int i=1;i+x-1<=n;i++){
if(sum[i+x-1]-sum[i-1]>=s) return true;
}
return false;
}
int solve(){
int l=1,r=n;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) r=mid-1;
else l=mid+1;
}
return l;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(sum,0,sizeof(sum));
scanf("%d%d",&n,&s);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum[i]+=sum[i-1]+a[i];//用sum[i]数组存储从1 到 i 的和
}
if(sum[n]<s)
printf("0\n");
else
printf("%d\n",solve());
}
}
/*
第二种是假设从s位置开始到t的和大于S,并且s + 1 到t' 的和大于S,则t‘ > t 由此可以跑一次o(n)解决
POJ 3061
*/
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 100005
int n,S;
int a[maxn];
void solve(){
int s=1,sum=0,pos=1;
int ans=n+1;
for(;;s++){
while(sum<=S&&pos<=n)
sum+=a[pos++];
if(sum<S) break;
sum-=a[s];
ans=min(ans,pos-s);
}
printf("%d\n",ans==n+1?0:ans);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&S);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
solve();
}
}
/*
题意:n头牛站成线,有朝前有朝后的的,然后每次可以选择大小为k的区间里的牛全部转向,会有一个最小操作m次使得它们全部面朝前方。问:求最小操作m,再此基础上求k。'
POJ 3276
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N;
int dir[5005];//牛的方向 0:F 1: B
int f[5005];// 区间[i,i+K-1] 是否进行反转
//固定K,求相应的最小操作回数
//无解返回-1
int clac(int K){
memset(f,0,sizeof(f));
int res=0;
int sum=0; //f 的和
for(int i=0;i+K<=N;i++){
//计算区间[i,i+K-1]
if((dir[i]+sum)%2!=0){
//前端的牛朝后方
res++;
f[i]=1;
}
sum+=f[i];
if(i-K+1>=0){
sum-=f[i-K+1];
}
}
//检查剩下的牛是否有朝着后方的情况
for(int i=N-K+1;i<N;i++){
if((dir[i]+sum)%2!=0) return -1;
if(i-K+1>=0)
sum-=f[i-K+1];
}
return res;
}
void solve(){
int K=1,M=N;
for(int k=1;k<=N;k++){
int m=clac(k);
if(m>=0 &&M>m){
M=m;
K=k;
}
}
printf("%d %d\n",K,M);
}
int main(){
char c;
while(~scanf("%d%*c",&N)){
for(int i=0;i<N;i++){
scanf("%c%*c",&c);
if(c=='F')
dir[i]=0;
else
dir[i]=1;
}
solve();
}
}
/*
题目大意:
某人读一本书,要看完所有的知识点,这本书共有P页,第i页恰好有一个知识点ai,(每一个知识点都有一个整数编号)。全书同一个知识点可能会被提到多次,他希望阅读其中一些连续的页把所有知识点都读到,给定每页所读到的知识点,求最少的阅读页数。
思路:
和上一题一样,也是尺取法的应用。
假设从某一页s开始阅读,为了覆盖所有的知识点读到t页,这样的话如果从s+1开始阅读,那么必须读到t'>=t位置,故可以用尺取法。
用上Map来统计次数,取出前一项要把对应的知识点的编号次数-1.详见代码。
POJ 3320
*/
#include<cstdio>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
int P;
int a[1000000+10];
map<int,int> countx; //知识点---出现次数 映射
set<int> all;
void solve(){
all.clear();
countx.clear();
for(int i=0;i<P;i++)
all.insert(a[i]);
int n=all.size();
int s=0,t=0,num=0;
int res=P;
while(true){
while(t<P&&num<n){
if(countx[a[t++]]++==0){ //出现新的知识点
num++;
}
}
if(num<n) break;
res=min(res,t-s);
if(--countx[a[s++]]==0){ //某个知识点出现次数为0
num--;
}
}
printf("%d\n",res);
}
int main(){
while(~scanf("%d",&P)){
for(int i=0;i<P;i++)
scanf("%d",&a[i]);
solve();
}
}
/*
题意是说,对四个数列中的数,每一列取一个,求取出来的四个数的和为零的组合个数。
POJ 2785
*/
#include<cstdio>
#include<algorithm>
#define MAXN 4005
using namespace std;
int n;
int A[MAXN],B[MAXN],C[MAXN],D[MAXN];
int CD[MAXN*MAXN];
void solve(){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
CD[i*n+j]=C[i]+D[j];
}
}
sort(CD,CD+n*n);
long long res=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
int cd=-(A[i]+B[j]);
res+=upper_bound(CD,CD+n*n,cd)-lower_bound(CD,CD+n*n,cd);
}
}
printf("%lld\n",res);
}
int main(){
while(~scanf("%d",&n)){
for(int i=0;i<n;i++)
scanf("%d%d%d%d",&A[i],&B[i],&C[i],&D[i]);
solve();
}
}
/*
【题目大意】:给出n辆赛车距离终点的距离,每秒钟会前进1米,现在给出m个可以加速k的加速器,每次每辆车只能使用一次加速器,下一个时间点加速器可以重复使用。问所有赛车到达终点的最短时间。
POJ 3232
*/
#include<cstdio>
int rider,dis[100005],acc,k;
bool check(int m){
long long tot=(long long)acc*m;
long long cnt=0;
for(int i=0;i<rider;i++){
if(dis[i]<=m) continue; //dis[i] <= m的话该车手自己就可以跑到终点
int tmp=(dis[i]-m)/(k-1)+((dis[i]-m)%(k-1)!=0);//tmp就是当前车手需要的加速次数,向上取整
if(tmp>m) return false; //注意!!这说明即使他一直在加速,一个加速器都无法满足该车手
cnt+=tmp;
if(cnt>tot) return false;//一旦超过总加速次数,说明次数不够
}
return true;
}
int main(){
int test;
scanf("%d",&test);
while(test--){
int maxi=0;
scanf("%d",&rider);
for(int i=0;i<rider;i++){
scanf("%d",&dis[i]);
if(dis[i]>maxi) maxi=dis[i];
}
scanf("%d%d",&acc,&k);
if(k>1){
int l=1,r=maxi,mid;
while(l<=r){
mid=(l+r)>>1;
if(check(mid))
r=mid-1;
else
l=mid+1; //注意!当check失败的时候,意味着需要更大的答案
}
printf("%d\n",l);
}
else printf("%d\n",maxi);
}
}