P1080 [NOIP2012 提高组] 国王游戏

written on 2022-05-22

所以我好不容易打了一大摞数学公式这题却交不了了??

第一道较为正式的贪心题吧。

思路历程:

  1. 这道题前40%的点都可以状压乱水,后面的点比较大,本来尝试用一些不一样的 dp ,但是乘积也极大,状态根本记不下,于是放弃 dp。

  2. 题目求问最大值最小,一下可以想到二分,但是对于判定却不能做到合理的时间复杂度分配,于是这个做法又寄了。

  3. 几种方法都不行,于是考虑贪心。贪心的典型套路是看相邻的两项能否交换。对于这道题,我们来具体分析一下:

考虑序列中的两项 ij (i<j),假设在这两项之前的所有乘积为 S,那么:

  1. ij 前,此时 val1=max(S÷yi,S×xi÷yj)

  2. ji 前,此时 val2=max(S÷yj,S×xj÷yi)

为方便起见,下面将 S÷yi 记为 AS×xi÷yj 记为 BS÷yj 记为 CS×xj÷yi 记为 D

比较两种情况,容易发现有 A<DB>C。若需要满足 i<j 为最优解(即 val1>val2),那么就一定有 B>D,即 S×xi÷yj>S×xj÷yi,移项,得到其充要条件为 xi×yi>xj×yj

于是按照这样的顺序排序,然后顺序扫一遍找最大值即可在 O(n) 的时间内获解。

注意还要写高精!


贪心代码

#include<bits/stdc++.h>
#define N 1005
using namespace std;
typedef long long ll;
int n;
struct F
{
	ll x,y,s;
}a[N];
bool cmp(F a,F b){return a.s<b.s;}
int now[10005],t[10005],ans[10005];
int Ans,len,lent;
bool check()
{
	if(Ans>lent) return 0;
	if(Ans<lent) return 1;
	for(int i=1;i<=lent;i++)
	{
		if(ans[i]<t[i]) return 1;
		if(ans[i]>t[i]) return 0;
	}
	return 0;
}
void update()
{
	Ans=lent;
	for(int i=1;i<=Ans;i++) ans[i]=t[i];
}
void divide(ll x)
{
	int jw=0;
	for(int i=1;i<=len;i++)
	{
		t[i]=(jw*10+now[i])/x;
		jw=(jw*10+now[i])%x;
	}
	int p=-1;
	for(int i=1;i<=len;i++)
		if(t[i]){p=i;break;}
	if(p==-1) lent=0;
	else lent=len-p+1;
	if(p==-1) return ;
	for(int i=1;i<=lent;i++) t[i]=t[p+i-1];
	//divide
}
void times(ll x)
{
	int jw=0;
	for(int i=1;i<=len;i++) now[i]*=x;
	for(int i=len;i;i--)
	{
		x=now[i]+jw;
		jw=x/10;
		now[i]=x%10;
	}
	lent=0;
	while(jw)
	{
		t[++lent]=jw%10;
		jw/=10;
	}
	for(int i=len+lent;i>=lent+1;i--) now[i]=now[i-lent];
	for(int i=1;i<=lent;i++) now[i]=t[lent-i+1];
	len+=lent;
}
void work()
{
	sort(a+1,a+1+n,cmp);
//	now=a[0].x;
	ll x=a[0].x;
	while(x)
	{
		t[++len]=x%10;
		x/=10;
	}
	for(int i=1;i<=len;i++) now[i]=t[len-i+1];
	for(int i=1;i<=n;i++)
	{
//		ans=max(ans,now/a[i].y);
		divide(a[i].y);
		if(check()) update();
//		times(now,a[i].x);
//		now*=a[i].x;
		times(a[i].x);
	}
	for(int i=1;i<=Ans;i++) printf("%d",ans[i]);
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<=n;i++)
	{
		scanf("%lld%lld",&a[i].x,&a[i].y);
		a[i].s=a[i].x*a[i].y;
	}
	work();
}

附40分代码(状压dp)

#include<bits/stdc++.h>
#define N 1005
using namespace std;
typedef long long ll;
int n;
ll ans;
struct F{ll x,y;}a[N];
ll f[(1<<20)+5],val[(1<<20)+5];
void work()
{
	f[0]=0;
	for(int i=0;i<(1<<n);i++)
	{
		val[i]=a[0].x;
		if(i) f[i]=1e18;
		for(int j=0;j<n;j++)
		{
			if(!(i&(1<<j))) continue;
			f[i]=min(f[i],max(f[i^(1<<j)],val[i^(1<<j)]/a[j+1].y));
			val[i]*=a[j+1].x;
		}
	}
	printf("%lld\n",f[(1<<n)-1]);
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<=n;i++) scanf("%lld%lld",&a[i].x,&a[i].y);
	work(); 
}

update on 2022-7-31

注意这题的贪心分析策略——邻项分析法,这在贪心题的分析中是很重要的。

posted @   Freshair_qprt  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示