【考题题解9】 贪心与枚举 组合数学+逆元 逆向思维模拟 贪心 二分答案+DP验证 暴力枚举或分块优化

1.贪心:

1.如果10元,找5元

2.如果20元

    a 优先5 和 10,因为尽量减少5,以免10不够找

    b 其次 5 5 5

#include
using namespace std;
int a[2000000]={};
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
}
int main()
{
	int n;
	n=read();
	for (int i=1;i<=n;i++)
	{
		int k;
		k=read();
		if (k==20)
		{
			if (a[5]>=1&&a[10]>=1) 
			{
				a[5]-=2;
				a[10]--;
				continue;
			}//5和10优先 
			else
			if (a[5]>=3)
			{
				a[5]-=3;
				continue;
			}
			else
			{
				cout<<"NO";
				return 0;
			}
		}
		if (k==10)
		{
			if (a[5]>=1)
			{
				a[5]--;
				a[10]++;
				continue; 
			}
			else 
			{
				cout<<"NO";
				return 0;
			}
		}
		if (k==5)
		    a[5]++;
	}
	cout<<"YES";
	return 0;
} 

2.可以看出,就是C(mn),也就是n选m 公式为:

      n!

————

m!(n-m)!

可以无视坐标

20分程序如下:

#include
using namespace std;
const long long Mod=1000000007;
inline long long sum(long long N)
{
	long long sum=1;
	for (int i=2;i<=N;i++)
	    sum=sum*i;
	return sum;
}
int main()
{
	long long n,k;
	cin>>n>>k;
	cout<<sum(n)/(sum(k)*sum(n-k));
}

至于取模,除法是不可以直接取模的,需要用到逆元

我不懂。。

AC代码如下

#include
using namespace std;
long long x,n,k,Mod=1000000007,a=1,b=1,c,i,ans;
long long power(long long s,long long sum)
{
    s=s%Mod;
    ans=1;
    while(sum)
    {
        if(sum&1) 
		  ans=(ans*s)%Mod;
        sum/=2;
        s=(s*s)%Mod;
    }
    return ans%Mod;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;i++)
     scanf("%d",&x);
    for(i=1;i<=n;i++)
     a=(a%Mod*i%Mod)%Mod;
    for(i=1;i<=k;i++)
     b=(b%Mod*i%Mod)%Mod;
    for(i=1;i<=(n-k);i++)
     b=(b%Mod*i%Mod)%Mod;
    c=power(b,Mod-2)%Mod; 
    printf("%d",(a%Mod*c%Mod)%Mod);
    return 0;
} 
3.一道思维题
逆向思维
把操作1和操作2,分别转化成:
1、将x变成x-1
2、将y,z合并成y+z
终止条件为只剩下一个数,且该数为0
贪心
只能通过减1操作把数变小,所以一个数在没有变成0不会与其他数合并。
因为如果有cnt个非零数,则大家一起减1,总和会变小cnt;
如果非零数合并了,则不优。

效率:O(n)。

#include
using namespace std;
int b[10000000]={};
int main()
{
	int n,ans=-1,m,x;
	cin>>n;
	for (int i=1;i<=n;i++)
	{
		cin>>x;
		m=max(m,x);
		b[x]++;
	}
	int t=b[0];
	for (int i=1;i<=m;i++)
	    t=(t+1)/2+b[i];//这句话表示剩下0的个数表示合并完的0和-1后为0的总和
	ans=m;//最少为最大数,即-1的次数
	while (t>1) t=(t+1)/2,ans++;//处理还剩下0的情况
	cout<<ans;
}

4.贪心

如果(a[i]<0) 不做任何处理

否则就算

枚举:

从高位到低位依次枚举:

1.当该二进制位为0 不作处理

2.否则

    a.当这位不取,则前面几位可以随便取,求前i-1为的最大值

    b.同时累加这一位

    c . b,c两步求最大值

#include
using namespace std;
long long n;
long long a[300000]={};
long long m[300000]={};
long long sum[300000]={};
long long s=0,ans=0;
char mm [1000000]={};
int main()
{
	cin>>n;
	for (long long i=1;i<=n;i++)   
	{
	    cin>>a[i];
	    if (a[i]>0) sum[i]=sum[i-1]+a[i];
	    else sum[i]=sum[i-1];//q前缀和
	}
	cin>>mm+1;
	for (long long i=1;i<=n;i++)
	    m[i]=mm[i]-'0';
    for (long long i=n;i>=1;i--)
    if (m[i])
    {
    	ans=max(ans,s+sum[i-1]);//求ab两步的最大值
    	s+=a[i];
    }
    cout<<max(ans,s);//最后别忘了判断哦
    return 0;
}

5.二分答案:mid

    表示每一个相邻的数字的差<=mid

DP验证:

    f[i]表示前i位保留的数字的最大值(前提是每个差<=mid)

    状态转移方程:f[i]=max(f[j]+1),条件是abs(a[i]-a[j])<=mid*(i-j),表示第i个数和第j个数相邻差满足条件

#include
using namespace std;
long long f[10000000]={};
long long n,k,in;
long long a[10000000]={};
inline bool check(long long x)
{
	memset(f,0,sizeof(f));
	for (long long i=1;i<=n;i++)
	{
		long long maxx=-100000000;
		f[i]=1;
		for (long long j=1;j<i;j++)
		    if ( abs(a[i]-a[j])<=x*(i-j) )
		        f[i]=max(f[i],f[j]+1);
	}
	long long in=-1;
	for (long long i=1;i<=n;i++) in=max(in,f[i]);
    return n-in<=k;
}
int main()
{
	cin>>n>>k;
	for (long long i=1;i<=n;i++)
	    cin>>a[i];
	memset(f,0,sizeof(f));
	long long left=0,right=9999999999,mid;
	while (left+1<right)
	{
		mid=(left+right)/2;
		if (check(mid)) right=mid;
		    else left=mid;
	}
	if (check(left)) cout<<left;
	    else cout<<right;
	return 0;
}

6.前缀和

枚举左端点右端点,复杂度O(n^2)

至于分块怎么优化

.....................


你可能感兴趣的:(【考题题解9】 贪心与枚举 组合数学+逆元 逆向思维模拟 贪心 二分答案+DP验证 暴力枚举或分块优化)