hiho226 Ctrl-C Ctrl-V

版权声明:转载我的原创博客情注明出处 https://blog.csdn.net/qq_31964727/article/details/83475547

目录

题目1 : Ctrl-C Ctrl-V

题目分析:

1.题是什么?

2.思路

3.ac代码

4.个人总结

5.我的错误解法


题目1 : Ctrl-C Ctrl-V

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

Yor are bored. So you open a notepad and keep pressing letter 'A' to type a lot of 'A's into the text area.

Suddenly an idea come out. If you can do the following 4 kinds of operations N times, how many 'A's you can type into the text area?    

- A: Type a letter A into the text area

- Ctrl-A: Select all the letters in the text area

- Ctrl-C: Copy the selected letters into clipboard  

- Ctrl-V: Paste the letters from clipboard into the text area

Assume N=7 you can get 9 'A's by doing following operations: A, A, A, Ctrl-A, Ctrl-C, Ctrl-V, Ctrl-V.  

输入

An integer N. (1 <= N <= 1000000000)

输出

The maximum number of 'A's you can type into the text area. Note the answer may be very large so output the answer modulo 1000000007.

样例输入

7

样例输出

9

题目分析:

1.题是什么?

    题就是给你一个十亿以内的数字n,代表你可以进行刚好n次以下操作,

    A操作:向当前字符串末尾添加一个字符'A'

    Ctrl-A操作:选定当前整个字符串

    Ctrl-C操作:把当前选定的字符串复制到剪贴板

    Ctrl-V操作:把剪贴板中的字符串粘贴到当前字符串末尾

   你要输出的就是通过n次操作你能作出的最长字符串有多长,结果取模1000000007

2.思路

    十亿的数据大小,别说暴力,n复杂度也不合适,故而猜测不同大小的n的答案之间必然有规律,或者说一定有一种简单的最优操作可以重复之然后达到最优答案,否则以低于n复杂度完成是不可能的.

    故而我们可以简单的思考一下最优解规律,首先

        (1).Ctrl-A+Ctrl-C的顺序是不可颠倒的,

        (2).Ctrl-A+Ctrl-C+Ctrl-V中间插入A操作无论插在哪儿必然不如A+Ctrl-A+Ctrl-C+Ctrl-V,可以自己分析一下,

        (3).Ctrl-A+Ctrl-V+Ctrl-C也必定不如Ctrl-A+Ctrl-C+Ctrl-V,因为Ctrl-V能贴出来的必定越来越长,大于等于之前的Ctrl-V效果,

        (4).经过简单的穷举可以知道当n<7时无论如何操作最优解为n,当n>=时最优解必存在Ctrl-A+Ctrl-C+Ctrl-V操作

     故而我们知道最优解中Ctrl-A+Ctrl-C+Ctrl-V必然是一个整体,Ctrl-A,Ctrl-C单独出现也是无意义的.n>=7时A操作一定在最前,因为在做了Ctrl-A+Ctrl-C之后每一个Ctrl-V操作肯定优于A操作,因为剪贴板里的长度肯定大于等于1.

    我自己的思路最开始是错了的,在个人总结中分析错误点,这里我给出官方的正确思路,其实关于答案是可以dp得到的,因为对于最优操作最后一个操作必然是A或者Ctrl-V,因为Ctrl-A,Ctrl-C并不实际增加长度,不可能放在最后一个,我们甚至可以这样子,前6个穷举直接赋值,因为当n>=7时最后一个操作必然是Ctrl-V,原因上面也总结了,故而可以得到dp的递推公式(n>=7):

                                操作是Ctrl-V:假设最优解最后有k个连续Ctrl-V,则dp[i]=dp[i-2-k]*(k+1);

        (dp[i-2-k]其实就是此时剪切板中字符串的长度,这里i之所以减去k+2是减去了k个连续的Ctrl-V以及前面连着的Ctrl-A+Ctrl-C,不减去前面连着的Ctrl-A+Ctrl-C你得到的可能不是剪切板中字符串的长度!,乘以k+1是k个粘贴来的加上原本的1个)

    初始状态的最优解穷举就可以得到,即n<7时最优解为n,之后每一步递推因为要穷举k的可能性,故而dp复杂度为O(n^{2}),十亿大小的数据自然不可能以此为解法,不过我们可以以此解法得出n为1到100的答案进而研究答案规律,答案如下:

void solve(){
	int n;
	scanf("%d",&n);
	//小于7时直接赋值
	for(int i=0;i<7;i++) dp[i]=i;
	for(int i=7;i<100;i++){
		dp[i]=0;
		//穷举K
		for(int k=i-3;k>0;k--) dp[i]=max(dp[i],dp[i-2-k]*(k+1)%mod); 
	}
	for(int i=1;i<100;i++){
		printf("%d %lld\n",i,dp[i]);
	}
}

 

    观察答案可得规律: n>=16时,dp[i] = dp[i-5]*4;换句话说,当n足够大的时候,最优的策略就是不断的用Ctrl-A+Ctrl-C+Ctrl-V+Ctrl-V+Ctrl-V这么5个操作把长度变成4倍.dp[i] = dp[x]*4^{k}(11<=x<= 15且i=x+5k),4^{k}必须使用快速幂算法,不知道快速幂算法的可以移步我的博客了解一下 快速幂算法

3.ac代码

#include <stdio.h>
typedef long long ll;
const ll mod=1000000007;
ll max(ll a,ll b){ return a>b?a:b; }

ll fastpower(ll a,ll b)     
{     
    ll res=1;     
    ll temp=a;     
    while(b){    
       if(b&1) res=res*temp%mod;     
       temp=temp*temp%mod;     
       b>>=1;  
    }
    return res;     
}

void solve(){
	int n;
	scanf("%d",&n);
	ll dp[16];
	//小于7时直接赋值
	for(int i=0;i<7;i++) dp[i]=i;
	for(int i=7;i<16;i++){
		dp[i]=0;
		//穷举K
		for(int k=i-3;k>0;k--) dp[i]=max(dp[i],dp[i-2-k]*(k+1)%mod); 
	}
	if(n<16) printf("%lld",dp[n]);
	else{
		int x=(n-11)%5+11;
		printf("%lld",dp[x]*fastpower(4,(n-x)/5)%mod); 
	}
}

int main(){
	solve();
	return 0;
}

4.个人总结

    我在看到这道题数据大小是十亿时第一时间就否定了dp思路,尽管这道题有很多dp的特征,最优解,线性变化,选择操作,初始解固定等等,可我仅仅因为数据大小不能执行dp就否认了dp思路这是错误的,即时最终答案不能直接dp出来,也许可以借用dp找到答案的规律,

    在否认dp思路之后我根据对最优解特征的分析得出了一种错误结论:最优解一定是前面全是A操作,中间全是Ctrl-A+Ctrl-C+Ctrl-V,结尾全部是Ctrl-V这样子的结构,其实这是错误的,前面全A是正确的,结尾全是Ctrl-V也是正确的,可是中间部分并不一定是连续的Ctrl-A+Ctrl-C+Ctrl-V,有一个反例:

    当n为11时,A+A+A+A+A+Ctrl-A+Ctrl-C+Ctrl-V+Ctrl-V+Ctrl-V+Ctrl-V是我的算法得到的答案,为25

    可是实际上存在更优解A+A+A+Ctrl-A+Ctrl-C+Ctrl-V+Ctrl-V+Ctrl-A+Ctrl-C+Ctrl-V+Ctrl-V,为27,

我固执的认为独立于Ctrl-A+Ctrl-C+Ctrl-V的单个的Ctrl-V都在末尾是错误的,虽然将这单个Ctrl-V后置后出来的一定是更多的,可是选择在某个Ctrl-A+Ctrl-C前面Ctrl-V会使后面的所有Ctrl-V收益更大,而相比较哪个大就不确定了,我正是忽略了这个,导致了以下错误解法

5.我的错误解法

void solve(){
	int n;
	scanf("%d",&n);
	if(n<7) printf("%d",n);
	else{
		ll ans=0;
		for(int i=1;i<20;i++){
			for(int j=0;j<20;j++){
				if(i+j<n&&(n-i-j)%3==0){
					ll temp=i;
					temp=i*kuaisumi(2,(n-i-j)/3)%mod;
					temp=(temp+j*(temp/2))%mod;
					if(temp>ans) ans=temp;
				}
			}
		}
		printf("%lld",ans);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_31964727/article/details/83475547