非精写版-51nod基础训练(持续更新)
Posted iuk11
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了非精写版-51nod基础训练(持续更新)相关的知识,希望对你有一定的参考价值。
题目就不赘述了,来源51nod网站。
最长公共子序列LCS
这道题是一道非常好想的二维dp问题,可以在不选当前行,不选当前列的情况下(dp[i+1][j+1]=dp[i][j]+1),若两个字符串中的字符相等,则有在原来的子序列基础上又增加一个字符;若两个字符串中的字符不相等,则寻找到此为止的最大的子序列的长度是多少,把值赋给它。
dp[i][j]:表示第一个字符串取(0,i-1),第二个字符串为(0,j-1),最长的公共子序列是多长。
最后在记录长度的同时,记录这个子序列的长度是怎么加过来的,记录完毕后从终点回溯到起点,用字符串保存回溯中找到的子序列的字符,翻转(因为是从终点到起点),输出。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int dp[maxn][maxn];
char opp[maxn][maxn];
int main(){
string s,str;
cin>>s>>str;
dp[0][0]=0;
for(int i=0;i<s.size();i++){
for(int j=0;j<str.size();j++){
if(s[i]==str[j]){
dp[i+1][j+1]=dp[i][j]+1;
opp[i][j]='x';
}else{
if(dp[i][j+1]>dp[i+1][j]){
dp[i+1][j+1]=dp[i][j+1];
opp[i][j]='d';
}else{
dp[i+1][j+1]=dp[i+1][j];
opp[i][j]='r';
}
}
}
}
string ans;
int x=s.size()-1,y=str.size()-1;
while(x>=0&&y>=0){
if(opp[x][y]=='x'){
ans+=s[x];
x--;
y--;
}else if(opp[x][y]=='d'){
x--;
}else{
y--;
}
}
reverse(ans.begin(),ans.end());
cout<<ans<<endl;
//system("pause");
return 0;
}
大数加法(考虑负数)
这就有点灵性了,乱写了一个,模拟的加减法竖式。
调了半天,没考虑前置零,考虑了之后又忘了还有结果为零的情况。
然后就分类讨论就可,我确实是写麻烦了,就当练练大模拟了。
#include<bits/stdc++.h>
using namespace std;
string add(string a,string b){
int fa=1,fb=1;
int lena=a.size(),lenb=b.size();
if(a[0]<'0'||a[0]>'9') fa=-1,a=a.substr(1,lena-1),lena--;
if(b[0]<'0'||b[0]>'9') fb=-1,b=b.substr(1,lenb-1),lenb--;
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
string ans;
int shi=0;
int tem=0;
int aa=0,bb=0;
ans="";
if((fa>0 && fb<0) || (fa<0 && fb>0)){
//a-b
if(lena>lenb || (lena==lenb && a[lena-1]>b[lenb-1])){
for(int i=0;i<lenb;i++){
aa=a[i]-'0';
bb=b[i]-'0';
tem=aa-bb+shi;
if(tem<0){
tem=tem+10;
shi=-1;
}else{
shi=0;
}
ans+=(tem+'0');
}
for(int i=lenb;i<lena;i++){
aa=a[i]-'0';
tem=aa+shi;
if(tem<0){
tem=tem+10;
shi=-1;
}else{
shi=0;
}
ans+=(tem+'0');
}
}else{
for(int i=0;i<lena;i++){
aa=a[i]-'0';
bb=b[i]-'0';
tem=bb-aa+shi;
if(tem<0){
tem=tem+10;
shi=-1;
}else{
shi=0;
}
ans+=(tem+'0');
}
for(int i=lena;i<lenb;i++){
bb=b[i]-'0';
tem=bb+shi;
if(tem<0){
tem=tem+10;
shi=-1;
}else{
shi=0;
}
ans+=(tem+'0');
}
ans+='-';
}
//b-a
if(fa<0 && fb>0){
int len=ans.size();
if(ans[len-1]=='-') ans=ans.substr(0,len-1);
else ans+='-';
}
}else{
//(a+b) or -(a+b)
if(lena>lenb){
for(int i=0;i<lenb;i++){
aa=a[i]-'0';
bb=b[i]-'0';
tem=aa+bb+shi;
shi=tem/10;
tem=tem%10;
ans+=(tem+'0');
}
for(int i=lenb;i<lena;i++){
aa=a[i]-'0';
tem=aa+shi;
shi=tem/10;
tem=tem%10;
ans+=(tem+'0');
}
}else{
for(int i=0;i<lena;i++){
aa=a[i]-'0';
bb=b[i]-'0';
tem=aa+bb+shi;
shi=tem/10;
tem=tem%10;
ans+=(tem+'0');
}
for(int i=lena;i<lenb;i++){
bb=b[i]-'0';
tem=bb+shi;
shi=tem/10;
tem=tem%10;
ans+=(tem+'0');
}
}
if(shi) ans+=(shi+'0');
if(fa<0) ans+='-';
}
int cnt=0;
int f=0;
int len=ans.size();
for(int i=len-1;i>=0;i--){
if(ans[i]=='-'){
f=1;
continue;
}
if(ans[i]=='0') cnt++;
else break;
}
if(f) cnt++;
ans=ans.substr(0,len-cnt);
if(ans==""){
return "0";
}
if(f) ans+='-';
reverse(ans.begin(),ans.end());
return ans;
}
int main(){
string a,b;
cin>>a>>b;
string ans;
ans=add(a,b);
cout<<ans<<endl;
//system("pause");
return 0;
}
day2
逆序对
- 方法一:归并排序求逆序对。
归并排序采用分而治之的思想,使复杂度降低到 O ( n l o g n ) O(nlog n) O(nlogn),在每一次回溯的过程中如果发现后面的数比前面的数大,就记录一下要向前移动多少位,思考一下为什么 p = l p=l p=l 和 q = m i d + 1 q=mid+1 q=mid+1。
#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
typedef long long ll;
ll a[maxn],b[maxn];
ll cnt;
void merge_sort(int l,int r){
if(r-l>0){
int mid=(l+r)/2;
int i=l;
int p=l,q=mid+1;
merge_sort(l,mid);
merge_sort(mid+1,r);
while(p<=mid || q<=r){
if(q>r||(p<=mid&&a[p]<=a[q])){
b[i++]=a[p++];
}else{
b[i++]=a[q++];
cnt=cnt+mid-p+1;
}
}
for(i=l;i<=r;i++){
a[i]=b[i];
}
}
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
merge_sort(1,n);
cout<<cnt<<endl;
//system("pause");
return 0;
}
- 方法二:树状数组求逆序对
看了一篇好博客
里面离散化解释的特别好,如果给定的数值很分散,跨度很大,我们就用一个新数组储存这些数值的下标,根据它们值的大小对下标排序。
思考:
接下来就是由大到小排序,(我们现在有的是对 a [ ] a[] a[]数组排好序的对应的下标数组 b [ ] b[] b[])按照这个逻辑,实际我们是在寻找数列中的正序对,x进入数组后要去找有多少个数是比它小的(因为如果有,一定是在x之前进入树状数组,一定是排在x之前的数,自然也一定是个正序对)。
那怎么找x之前有多少个比它小的数,在每个数x放入数组之时,更新[x,n]中的所有二的整数倍的数组下标中的值,记录在t[]数组中。
那么 t [ x ] t[x] t[x] 就是在[1,x]中已经存在了多少个小于x的数(好像也不应该这么说,[x+1,n]也更新了,但是我们只用到[1,x]的区间)。
#include<bits/stdc++.h>
using namespace std;
const int maxn=50010;
typedef long long ll;
ll a[maxn],b[maxn];
ll ans;
ll t[maxn];
int n;
int lowbit(int x){
return x&-x;
}
void add(int x){
while(x<=n){
t[x]++;
x+=lowbit(x);
}
}
ll sum(int x){
ll res=0;
while(x>=1){
res+=t[x];
x-=lowbit(x);
}
return res;
}
bool cmp(int x,int y){
if(a[x]==a[y]) return x>y;
return a[x]>a[y];
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=i;
}
sort(b+1,b+1+n,cmp);
f以上是关于非精写版-51nod基础训练(持续更新)的主要内容,如果未能解决你的问题,请参考以下文章