cunzai_zsy0531

关注我

P2549 计算器写作文 题解

Post time: 2020-12-01 19:37:35

题目链接

我们首先考虑把两个单词合起来的运算定义为 \(+\),那么如果 \(A+B>B+A\),无论怎么合并,我们都一定会把 \(A\) 放在 \(B\) 的前面。

于是我们可以通过这样一个判定方法将所有的单词排序,然后跑背包。即设 \(f_{i,j}\) 表示前 \(i\) 个单词,长度为 \(j\) 时能够得到的最大值。那么有:

\[f_{i,j}=\max(f_{i-1,j},f_{i-1,j-a_i.len}+a_i ) \]

注意这里的 \(+\) 就是上面我们定义的 \(+\),与此同时,\(\max\) 运算也需要特殊比较,方法是这样:

bool operator >(const Big &A)const{
	if(len!=A.len) return len>A.len;
	for(int i=1;i<=len;++i)
             if(d[i]!=A.d[i]) return d[i]>A.d[i];
  	return 0;
}

就比较像高精度里边的比较大小。

但是这样的话并不能通过所有的数据,为什么呢?

我们发现,这个做法在个位为 \(0\) 时不成立,因为小数的大小比较不比较长度,只比较每一位。也就是说我们需要判断,是不是所有的单词都以 \(0\) 开头。这时候我们前面的排序也需要按照这种新方式,然后同样跑一个背包。\(\max\) 运算的比较方法变成了:

bool operator <(const Big &A)const{
	int tmp=min(len,A.len);
	for(int i=1;i<=tmp;++i)
		if(d[i]!=A.d[i]) return d[i]<A.d[i];
	return len<A.len;
}

这样我们就可以通过这道题。

这个题的单词,我是通过 struct 定义了大整数类型来存储的。这样做有很大的优势,因为可以通过 operator 重载运算符,使代码非常简洁。建议大家尝试。

总结一下思考这类题的方式:首先把 dp 方程设出来,发现是一个背包,背包 dp 需要考虑顺序问题,它无法处理顺序可能不同的选择。所以我们根据两个单词合并的大小得到了顺序,可以跑背包了。然后我们发现 \(0\) 开头所组成的小数,因为判定大小的方法不同而需要重新处理。最终我们综合以上想法可以通过此题。

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
const int N=200+13,M=10000+13;
struct Big{//使用了struct自定义类型存储,优势明显
	int d[N],len;
	Big(){memset(d,0,sizeof d);len=0;}//初始化
	Big operator +(const Big &A)const{
		Big Ans;
		for(int i=1;i<=len;++i) Ans.d[i]=d[i];
		for(int i=1;i<=A.len;++i) Ans.d[i+len]=A.d[i];
		Ans.len=len+A.len;
		return Ans;
	}
	bool operator >(const Big &A)const{//第一种比较方式
		if(len!=A.len) return len>A.len;
		for(int i=1;i<=len;++i)
			if(d[i]!=A.d[i]) return d[i]>A.d[i];
		return 0;
	}
	bool operator <(const Big &A)const{//第二种,整数位为0时的比较方式
		int tmp=min(len,A.len);
		for(int i=1;i<=tmp;++i)
			if(d[i]!=A.d[i]) return d[i]<A.d[i];
		return len<A.len;
	}
}a[M],f[N];
ostream &operator <<(ostream &output,Big A){//并不非常常见的重载输出
	if(A.d[1]==0){
		output<<"0.";
		for(int i=2;i<=A.len;++i) output<<A.d[i];
	}
	else for(int i=1;i<=A.len;++i) output<<A.d[i];
	output<<endl;return output;
}
int n,m;
map<char,int> ms;
inline void init(){
	ms['O']=ms['D']=0,ms['G']=9,ms['B']=8,ms['L']=7,
	ms['q']=6,ms['S']=5,ms['h']=4,ms['E']=3,ms['Z']=2,ms['I']=1;
}
inline bool cmp1(Big A,Big B){return (A+B)>(B+A);}
inline bool cmp2(Big A,Big B){
	int tmp=min(A.len,B.len);
	for(int i=1;i<=tmp;++i)
		if(A.d[i]!=B.d[i]) return A.d[i]>B.d[i];
	return A.len>B.len;
}
inline void solve1(){
	sort(a+1,a+n+1,cmp1);
	for(int i=1;i<=n;++i)
	for(int j=m;j>=a[i].len;--j){
		Big Tmp=f[j-a[i].len]+a[i];
		if(Tmp>f[j]) f[j]=Tmp;
	}
	cout<<f[m];
}
inline void solve2(){
	sort(a+1,a+n+1,cmp2);
	for(int i=1;i<=n;++i){
		for(int j=m;j>=a[i].len;--j){
			Big Tmp=f[j-a[i].len]+a[i];
			if(f[j]<Tmp) f[j]=Tmp;
		}
	}
	cout<<f[m];
}
int main(){
	init();//预处理字母与数字的对应关系,用map比较容易实现
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;++i){
		char s[40];
		scanf("%s",s+1);
		int l=strlen(s+1);
		a[i].len=l;
		for(int j=l;j>=1;--j) a[i].d[l-j+1]=ms[s[j]];
	}
	bool state=0;
	for(int i=1;i<=n;++i)
		if(a[i].d[1]>0){state=1;break;}
	if(state) solve1();//需要不同的大小判断,所以分两种不同的方式解决
	else solve2();
	return 0;
}
posted @ 2022-04-21 11:40  cunzai_zsy0531  阅读(34)  评论(0编辑  收藏  举报