4.7总结
1.FBI树(不该犯的重大问题:看清输入格式!!!)
一开始要先理清思路:对每个树结点判断F/B/I树,不会超时。
对一个01字符串,每次二分即可求得子树。
后序遍历,只需按照左-右-根的顺序建树并输出,二者可以放在同一函数里。
#includeusing namespace std; string s; void maketree(int l,int r){ if(r>l){ maketree(l,(l+r)/2); maketree((l+r+1)/2,r); } int B=1,I=1; for(int i=0;i<=r-l;i++){ if(s[l+i]=='1'){ B=0; }else if(s[l+i]=='0'){ I=0; } } if(B) cout<<'B'; else if(I) cout<<'I'; else cout<<'F'; } int main(){ freopen("fbi.in","r",stdin); freopen("fbi.out","w",stdout); int n; cin>>n>>s; maketree(0,s.size()-1); return 0; }
2.货币系统
给出所需面值和已有货币面值,求有几种组成方案。
这个题卡在(顺序不同,但量相同)的去重上了,其实一开始的思路正确,后面浪费了很多不必要的时间。
这类题两种DP方式,“加法”和“减法”,这个题用加法思路会更明确。假如F[j]表示面值j的方案数,f[j]+=f[j-a[i]] 这个式子能够保证会取到所有的方案数()
#include#define FOR(i,a,b) for(long long i = (a);i <= (b);i++) using namespace std; int a[10005],f[10005],b[10005],n,m,ans; int main(){ freopen("money.in","r",stdin); freopen("money.out","w",stdout); scanf("%d%d",&n,&m); FOR(i,1,n) scanf("%d",&a[i]); f[0]=1; FOR(i,1,n) FOR(j,a[i],m) f[j]+=f[j-a[i]]; cout< endl; return 0; }
3.合并果子
老生常谈的问题了,不过,很快能想到的优先队列维护不是最优解,O(n)做法如下:
#include#define FOR(i,a,b) for(int i = (a);i <= (b);i++) using namespace std; queue<long long> q1,q2; long long tong[100005],n,ans; int main(){ freopen("fruit.in","r",stdin); freopen("fruit.out","w",stdout); cin>>n; FOR(i,1,n){ int x; scanf("%d",&x); tong[x]++; } FOR(i,1,100000){ while(tong[i]){ tong[i]--; q1.push(i); } } FOR(i,1,n-1){ long long x,y; if((q1.front() q2.empty()) { x=q1.front(); q1.pop(); } else { x=q2.front(); q2.pop(); } if((q1.front() q2.empty()) { y=q1.front(); q1.pop(); } else { y=q2.front(); q2.pop(); } ans+=(x+y);//ans加值 q2.push(x+y);//将合并的果子加入到q2。 } printf("%d",ans); return 0; }
队列q1存放正常果子,q2存放合并后的果子,取两个队列中的最小与次小值。好处就是两个队列都不需要额外排序,(当然预处理需要桶排)按此流程节省排序时间。