NOIP2017模拟(忘了哪一天)

2 篇文章 0 订阅
2 篇文章 0 订阅

T1  energy

题目大意:

小美为了拯救世界能源危机,她准备了 n 台蓄电池。
一开始每台蓄电池有 ai 个单位的能量。
现在她想把 n 台蓄电池调整到能量相同。
对于每台蓄电池可以给另一台蓄电池传递能量。
但是会有能量损耗,每次给 x 个单位的能量只能接受到 x-((x*k)/100)的能量。
k 是损耗参数。
小美想知道每个蓄电池最多能同时到多少能量。

输入:

第一行两个整数 n,k。
第二行 n 个数表示 ai

输出:

输出一行保留 6 位小数表示答案。

分析:

二分答案。直接二分最后的能量,显然比初始能量比最后能量多的电池一定要将能量输出,同理初始能量少的也一定要接受能量。

如果输出能量乘以损耗系数大于接收能量,则将左指针右移,反之左移。由于最后要求实数,二分时可以指定次数,防止实数精度

的问题。不过还有一个有用的小技巧可以直接处理这道题的精度问题,就是直接将每个电池的能量乘以1e7,最后再除以1e7(题目要求保留6位小数)。

反思:

考试的时候没有想到二分答案。。。不过好像除了我所有人都想到了,只有我莫名其妙推了个数学表达式出来(实际上解释了本题二分答案的正确性,但我没有意识到),

企图直接O(1)计算。还是太navie(orz..)。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
#include<cmath>
using namespace std;
long long n,a[1000001];
double k;
long long l=1e18+7,r,mid;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cin>>n>>k;
	k=(1-k/100.0);
	for(int i=1;i<=n;++i)
	{
		cin>>a[i];
		a[i]*=10000000;
		l=min(l,a[i]);
		r=max(r,a[i]);
	}
	sort(a+1,a+1+n);
	while(l<r)
	{
		long long give=0,need=0;
		mid=(l+r)>>1;
		if(mid==l) break;
		for(int i=1;i<=n;++i)
		{
			if(a[i]>mid) break;
			else need+=mid-a[i];
		}
		for(int i=n;i>=1;--i)
		{
			if(a[i]<mid) break;
			else give+=a[i]-mid;
		}
		if(give*k>=need)
		  l=mid;
		else
		  r=mid;
 }
	double ans=l/10000000.0;
	printf("%0.6lf",ans);
	return 0;
}
T2 movie

题目大意:

小美去看电影。
她发现这个电影票很神奇啊,有一个编号 (x,y)。
表示为第 x 排第 y 位。
小美是个聪明的女孩子,她有自己的一套对于幸运的编号的定义:
(a,b) 如果是幸运的,那么 a*b=rev(a)*rev(b),a>0,b>0 。
rev(x) 的定义是把 x 的十进制的数字翻转,比如:
rev(20010)=1002,rev(1010)=101。
现在她想要至少 w 张幸运的电影票,问座位至少有几个。
座位个数为 max(a)*max(b)。
且要保证 max(a) ≤ maxa 和 max(b)) ≤ maxb 。maxa 和 maxb 是指定的数。

输入:

输入一行,有 3 个整数 maxa,maxb,w 。

输出:

输出最少的座位个数,如果无解输出 -1 。

分析:

先暴力搜索行或者列(这里选择行),先通过求gcd算出每个a所对应的最简分数比,然后储存在map中计算次数;

行搜索完与w比较,判断是否合法,最后再一列一列地减。

代码:

#include <cstdio>
#include <algorithm>
#include <map>
#include <cstring>
#include<iostream>
using namespace std;
map<double,int> xx;
map<double,int> yy;
long long ans,i,j;
int n,m,k;
double c[1000005],g[1000005];
int gcd(int a,int b)
{
	if(!a) return b;
	else return gcd(b%a,a);
}
long long re(long long x)
{
	long long k=x,a=0;
	while(x)
	{
		a=a*10+x%10;
		x/=10;
	}
	return a;
}
int main()
{
	cin>>n>>m>>k;
	for(i=1;i<=max(n,m);++i)
	{
		int l=gcd(i,re(i));
		c[i]=(double)(i/l)/(re(i)/l);
		g[i]=(double)(re(i)/l)/(i/l);
	}
	for(i=1;i<=min(m,n);++i)
	{
		xx[c[i]]++;
	}
	i=min(n,m);
	int h=0;
	for(j=0;j<max(m,n)&&h<k;)
	{
		j++;
		yy[c[j]]++;
		h+=xx[g[j]];
	}
	if(h<k) {cout<<"-1"<<endl;return 0;}
	ans=i*j;
	for(;i;--i)
	{
		xx[c[i]]--;
		h-=yy[g[i]];
		for(;j<max(m,n)&&h<k;)
		{
			j++;
			h+=xx[g[j]];
			yy[c[j]]++;
		}
		if(h<k) break;
		ans=min(ans,(i-1)*j);
	}
	cout<<ans<<endl;
	return 0;
}
T3 flow

题目大意:

题目是这样的,有 n 个基站,有 n*(n−1)/2 条边,每条边都有一些属性:s,f,l,h,a 。

表示这条边是从 s 流向 f 的而且 s<f ,流量必须是非负整数,而且这个流量在 [l,h] 的范围内。

设流量为 x ,那么这条边的利润就是 a+x*x。

如果这条边流量是 0 ,那么利润则是 0 。

水流从 1 号基站流出,流向 n 号基站。

那么 [2,n−1] 中的所有基站流进的水流总和必须等于流出的水流总和。

小美想知道最少的从 1 流出的流量是多少,同时想知道保证最少流量的同时求最大的利润。

输入:

第一行一个正整数 n 表示点数。
接下来 n*(n−1)/2 行,每行 5 个数表示这条边的属性:s,f,l,h,a 。

输出:

输出一行包含两个数:最少的流量和最大的利润。如果没有流量可以满足边的需求则输出“-1 -1”。

分析:

强行扯上网络流,但实际上网络流不可做。。一道神奇的搜索题,据说很符合NOIP的搜索题。。(orz..)现在还不太明白。

代码:

#include <cstdio>
#include <algorithm>
#include <map>
#include <cstring>
#include<iostream>
using namespace std;
struct node{
	int l,r,a;
}f[20][20];
int n,wx[20];
int now_a,now_b,ans_a,ans_b;
int read()
{
	int x=0;
	char ch;
	int f=1;
	for(ch=getchar();ch!='-'&&(ch<'0'||ch>'9');ch=getchar());
	if(ch=='-')
	{
		f=-1;
		ch=getchar();
	}
	for(;ch>='0'&&ch<='9';ch=getchar())
	  x=(x<<1)+(x<<3)+ch-'0';
	return x*f;
}
void dfs(int x,int u,int v);
void work(int x)
{
	if(x==n)
	{
		if(ans_a==now_a) ans_b=max(ans_b,now_b);
		if(ans_a>now_a) {ans_b=now_b;ans_a=now_a;}
		return;
	}
	dfs(x,x+1,wx[x]);
}
void dfs(int x,int u,int v)
{
	if(u==n+1&&v==0) {work(x+1);return;}
	if(u==n+1||v<0) return;
	for(int i=f[x][u].l;i<=f[x][u].r;++i)
	{
		wx[u]+=i;
		if(i) now_b+=i*i+f[x][u].a;
		dfs(x,u+1,v-i);
		wx[u]-=i;
		if(i) now_b-=i*i+f[x][u].a;	
	}
}
void dfs(int x,int u)
{
	if(now_a>ans_a) return;
	if(u==n+1) {work(x+1);return;}
	for(int i=f[x][u].l;i<=f[x][u].r;++i)
	{
		wx[u]+=i;
		now_a+=i;
		if(i) now_b+=i*i+f[x][u].a;
		dfs(x,u+1);
		wx[u]-=i;
		now_a-=i;
		if(i) now_b-=i*i+f[x][u].a;
	}
}
int main()
{
	n=read();
	ans_a=100000;
	for(int i=1;i<=n*(n-1)/2;++i)
	{
		int u,v;
		u=read();
		v=read();
		f[u][v].l=read();
		f[u][v].r=read();
		f[u][v].a=read();
	}
	dfs(1,2);
	if(ans_a==100000) ans_a=ans_b=-1;
	cout<<ans_a<<" "<<ans_b<<endl;
	return 0;
}
总结:题目性质把握还不太准确,要多加练习,二分答案以后一定不能再被坑了。思路一定要打开,不能死磕。


输出一行包含两个数:最少的流量和最大的利润。如果没有流量可以满足边的需求则输出“-1 -1”。
输出一行包含两个数:最少的流量和最大的利润。如果没有流量可以满足边的需求则输出“-1 -1”。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值