第四章 算法初步
4.1 排序
【例】A1062 Talent and Virtue (25 分)
ATTENTION
- 水题,但是你的笔误真的有点多。要多注意下笔。
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
struct per{
int id;
int vgrade,tgrade;
per(){}
per(int i,int v,int t){id=i;vgrade=v;tgrade=t;}
};
vector<per> sage,noble,fool,remain;
int n,l,h;
bool cmp(per &a,per &b)
{
if((a.tgrade+a.vgrade)==(b.vgrade+b.tgrade))
{
if(a.vgrade==b.vgrade)
return a.id<b.id;
return a.vgrade>b.vgrade;
}
return (a.tgrade+a.vgrade)>(b.vgrade+b.tgrade);
}
int main()
{
scanf("%d %d %d",&n,&l,&h);
for(int i=0;i<n;i++)
{
int id,v,t;
scanf("%d %d %d",&id,&v,&t);
per tmp(id,v,t);
if(v>=h&&t>=h) sage.push_back(tmp);
else if(v>=h&&t<h&&t>=l) noble.push_back(tmp);
else if(t>=l&&v>=t&&v>=l) fool.push_back(tmp);
else if(t>=l&&v>=l) remain.push_back(tmp);
}
sort(sage.begin(),sage.end(),cmp);
sort(noble.begin(),noble.end(),cmp);
sort(fool.begin(),fool.end(),cmp);
sort(remain.begin(),remain.end(),cmp);
printf("%d\n",sage.size()+noble.size()+fool.size()+remain.size());
for(int i=0;i<sage.size();i++)
printf("%d %d %d\n",sage[i].id,sage[i].vgrade,sage[i].tgrade);
for(int i=0;i<noble.size();i++)
printf("%d %d %d\n",noble[i].id,noble[i].vgrade,noble[i].tgrade);
for(int i=0;i<fool.size();i++)
printf("%d %d %d\n",fool[i].id,fool[i].vgrade,fool[i].tgrade);
for(int i=0;i<remain.size();i++)
printf("%d %d %d\n",remain[i].id,remain[i].vgrade,remain[i].tgrade);
return 0;
}
【例】A1012 The Best Rank (25 分)
ATTENTION
- 按理说也是道水题,但是debug了很久,很多小错误。
- 对于这种对限定范围排序的题目,尤其不是从0开始的,要注意每个for循环的界限。这道题就是因为四个
sort
以及对应的for
循环,和最后一个输出的for
循环的界限有问题,才有很多wa。 - 排序:貌似PAT例默认的是,同分同名次,但排名根据人数走,比如:99 98 98 97,那么排名为1 2 2 4。
- 对于**
id
和idx
的映射**,由于这道题的范围比较小,可以开数组形成映射。代码里是通过map
实现映射的。 - 考场上要细心一点!
#include <string>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
int n,m;
struct per{
int id;
int cg,mg,eg,ag;
int bestRank;
char bestLan;
per() { cg=mg=eg=ag=bestRank=1e9; }
per(int i,int c,int m,int e,int a)
{
id=i;cg=c;mg=m;eg=e;ag=a;
bestRank=1e9;
}
};
vector<per> stu;
map<int,int> nameToIdx;
bool cmp1(per &a,per &b){return a.ag>b.ag;}
bool cmp2(per &a,per &b){return a.cg>b.cg;}
bool cmp3(per &a,per &b){return a.mg>b.mg;}
bool cmp4(per &a,per &b){return a.eg>b.eg;}
int main()
{
scanf("%d %d",&n,&m);
per tmp(0,0,0,0,0); //0不用
stu.push_back(tmp);
for(int i=1;i<=n;i++)
{
int id,c,m,e;
scanf("%d %d %d %d",&id,&c,&m,&e);
int a=(c+m+e)/3;
nameToIdx[id]=i;
per tmp(id,c,m,e,a);
stu.push_back(tmp);
}
sort(stu.begin()+1,stu.end(),cmp1);
int pre=0,idx=0;
for(int i=1;i<=n;i++)
{
if(i==1)
{
idx=i;
pre=1;
}
else
{
if(stu[i].ag==stu[i-1].ag)
idx=pre;
else
idx=i;
}
pre=idx;
if(stu[i].bestRank>idx)
{
stu[i].bestRank=idx;
stu[i].bestLan='A';
}
}
sort(stu.begin()+1,stu.end(),cmp2);
pre=0;idx=0;
for(int i=1;i<=n;i++)
{
if(i==1)
{
idx=i;
pre=1;
}
else
{
if(stu[i].cg==stu[i-1].cg)
idx=pre;
else
idx=i;
}
pre=idx;
if(stu[i].bestRank>idx)
{
stu[i].bestRank=idx;
stu[i].bestLan='C';
}
}
sort(stu.begin()+1,stu.end(),cmp3);
pre=0;idx=0;
for(int i=1;i<=n;i++)
{
if(i==1)
{
idx=i;
pre=1;
}
else
{
if(stu[i].mg==stu[i-1].mg)
idx=pre;
else
idx=i;
}
pre=idx;
if(stu[i].bestRank>idx)
{
stu[i].bestRank=idx;
stu[i].bestLan='M';
}
}
sort(stu.begin()+1,stu.end(),cmp4);
pre=0;idx=0;
for(int i=1;i<=n;i++)
{
if(i==1)
{
idx=i;
pre=1;
}
else
{
if(stu[i].eg==stu[i-1].eg)
idx=pre;
else
idx=i;
}
pre=idx;
if(stu[i].bestRank>idx)
{
stu[i].bestRank=idx;
stu[i].bestLan='E';
}
}
for(int i=0;i<m;i++)
{
int id;
scanf("%d",&id);
vector<per>::iterator it;
for(it=stu.begin()+1;it!=stu.end();it++)
{
if(it->id==id)
break;
}
if(it!=stu.end())
printf("%d %c\n",it->bestRank,it->bestLan);
else
printf("N/A\n");
}
return 0;
}
【例】A1025 PAT Ranking (25 分)
simultaneously adv. 同时地
merge vi. 合并;融合
ATTENTION
- 水题。输入的同时得到每个考场的
rank
。最后再sort
所有stu
,得到TOTRank
。
//simultaneously merge
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int n,k,cnt=0;
struct per{
string id;
int score,localIdx,localRank,totRank;
per(){}
per(string d,int li,int s){id=d;localIdx=li;score=s;}
};
vector<per> stu;
bool cmp(per &a,per &b)
{
if(a.score==b.score)
return a.id<b.id;
return a.score>b.score;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>k;
for(int j=1;j<=k;j++)
{
string id;int s;
cin>>id>>s;
per tmp(id,i,s);
stu.push_back(tmp);
}
sort(stu.begin()+cnt,stu.begin()+cnt+k,cmp);
int pre=1;
for(int j=cnt;j<cnt+k;j++)
{
if(j==cnt) stu[j].localRank=1;
else
{
if(stu[j].score!=stu[j-1].score)
stu[j].localRank=j-cnt+1;
else
stu[j].localRank=pre;
}
pre=stu[j].localRank;
}
cnt+=k;
}
sort(stu.begin(),stu.end(),cmp);
int pre=1;
for(int i=0;i<stu.size();i++)
{
if(i==1) stu[i].totRank=pre;
else
{
if(stu[i].score==stu[i-1].score)
stu[i].totRank=pre;
else
stu[i].totRank=i+1;
}
pre=stu[i].totRank;
}
cout<<cnt<<"\n";
for(int i=0;i<stu.size();i++)
cout<<stu[i].id<<" "<<stu[i].totRank<<" "<<stu[i].localIdx<<" "<<stu[i].localRank<<"\n";
return 0;
}
【例】A1028 List Sorting (25 分)
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int n,c;
struct per{
int grade;
string id,name;
per(string n,string d,int g) { name=n;id=d;grade=g; }
};
vector<per> stu;
bool cmp1(per &a,per &b)
{
return a.id<b.id;
}
bool cmp2(per &a,per &b)
{
if(a.name==b.name)
return a.id<b.id;
return a.name<b.name;
}
bool cmp3(per &a,per &b)
{
if(a.grade==b.grade)
return a.id<b.id;
return a.grade<b.grade;
}
int main()
{
cin>>n>>c;
for(int i=0;i<n;i++)
{
string n,d;int s;
cin>>d>>n>>s;
per tmp(n,d,s);
stu.push_back(tmp);
}
if(c==1)
{
sort(stu.begin(),stu.end(),cmp1);
for(int i=0;i<stu.size();i++)
cout<<stu[i].id<<" "<<stu[i].name<<" "<<stu[i].grade<<"\n";
}
else if(c==2)
{
sort(stu.begin(),stu.end(),cmp2);
for(int i=0;i<stu.size();i++)
cout<<stu[i].id<<" "<<stu[i].name<<" "<<stu[i].grade<<"\n";
}
else if(c==3)
{
sort(stu.begin(),stu.end(),cmp3);
for(int i=0;i<stu.size();i++)
cout<<stu[i].id<<" "<<stu[i].name<<" "<<stu[i].grade<<"\n";
}
return 0;
}
【例】A1055 The World’s Richest (25 分)
ATTENTION
- 这道题可以做预处理,即只存储某个年龄的前100位最优财富的人。
- PAT卡时间卡的很死。有时候,只有最佳方法才能ac。在考试的时候,对于这些点,要在数据量极大的情况下去考虑,否则,比如这道题,在数据量小的时候去考虑只存储前100个就很难想到。
#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int n,k,m;
struct per{
string name;
int age,worth;
per(){}
per(string n,int a,int w){name=n;age=a;worth=w;}
};
vector<per> big;
bool cmp(per &a,per &b)
{
if(a.worth==b.worth)
{
if(a.age==b.age)
return a.name<b.name;
return a.age<b.age;
}
return a.worth>b.worth;
}
int main()
{
scanf("%d %d",&n,&k);
string name;
int age,worth;
for(int i=0;i<n;i++)
{
cin>>name;
scanf("%d %d",&age,&worth);
per tmp(name,age,worth);
big.push_back(tmp);
}
sort(big.begin(),big.end(),cmp);
int mmin,mmax;
for(int i=1;i<=k;i++)
{
int cnt=0;
scanf("%d %d %d",&m,&mmin,&mmax);
printf("Case #%d:\n",i);
for(int j=0;j<big.size();j++)
{
if(cnt==m) break;
if(big[j].age>=mmin&&big[j].age<=mmax)
{
printf("%s %d %d\n",big[j].name.c_str(),big[j].age,big[j].worth);
cnt++;
}
}
if(cnt==0)
printf("None\n");
}
}
【例】A1083 List Grades (25 分)
with respect to 关于
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
//with respect to 关于
struct PER{
string name,id;
int grade;
PER(string n,string i,int g)
{
name=n;
id=i;
grade=g;
}
};
vector<PER> stu;
int n,g1,g2;
bool cmp(PER a,PER b)
{
return a.grade>b.grade;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
string s1,s2;int g;
cin>>s1>>s2>>g;
PER tmp(s1,s2,g);
stu.push_back(tmp);
}
cin>>g1>>g2;
bool flag=false;
sort(stu.begin(),stu.end(),cmp); //
for(int i=0;i<stu.size();i++)
{
if(stu[i].grade<=g2&&stu[i].grade>=g1)
{
flag=true;
cout<<stu[i].name<<" "<<stu[i].id<<endl;
}
}
if(!flag)
cout<<"NONE";
return 0;
}
*【例】A1089 Insert or Merge (25 分)
//adjacent resule
//插入排序和归并排序其实都可以用sort实现
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int n;
int ins[110],mer[110],ans[110];
void insertSort(int i)
{
int idx=i,tmp=ins[i];
for(int j=1;j<i;j++)
{
if(ins[i]<ins[j])
{
idx=j;
break;
}
}
for(int j=i-1;j>=idx;j--)
ins[j+1]=ins[j];
ins[idx]=tmp;
}
bool isEqual(int cur[],int n)
{
for(int i=1;i<=n;i++)
if(cur[i]!=ans[i]) return false;
return true;
}
void mergeSort(int t)
{
int len=t;
for(int i=1;i<=n;i+=len)
{
int end=min(i+len,1+n);
sort(mer+i,mer+end);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&ins[i]);
mer[i]=ins[i];
}
for(int i=1;i<=n;i++)
scanf("%d",&ans[i]);
for(int i=2;i<=n;i++)
{
insertSort(i);
if(isEqual(ins,n))
{
printf("Insertion Sort\n");
insertSort(i+1);
for(int j=1;j<=n;j++)
{
if(j==1) printf("%d",ins[j]);
else printf(" %d",ins[j]);
}
}
}
int cnt=2;
while(cnt<=n)
{
mergeSort(cnt);
cnt*=2;
if(isEqual(mer,n))
{
printf("Merge Sort\n");
mergeSort(cnt);
for(int j=1;j<=n;j++)
{
if(j==1) printf("%d",mer[j]);
else printf(" %d",mer[j]);
}
}
}
return 0;
}
4.2 散列
【例】A1084 Broken Keyboard (20 分)
detect vt. 察觉;发现;探测
captilize 首字母大写其余字母小写
ATTENTION
- 像这种数据量很小的题,直接用数组映射即可。
- 先枚举第二个字符串,将出现过的字符设为1。再枚举第一个字符串,没出现过的字符就是无法输出的字符。为了不重复输出,在检测到一个不能输出的字符后,将其也设为1。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
string s1,s2;
bool vis[100]={0}; //0-9 A-Z _
vector<char> ans;
int main()
{
cin>>s1>>s2;
for(int i=0;i<s2.size();i++)
{
if(s2[i]>='a'&&s2[i]<='z')
s2[i]=s2[i]-'a'+'A';
if(s2[i]>='0'&&s2[i]<='9')
vis[s2[i]-'0']=1;
else if(s2[i]>='A'&&s2[i]<='Z')
vis[s2[i]-'A'+20]=1;
else
vis[50]=1;
}
for(int i=0;i<s1.size();i++)
{
int idx=0;
if(s1[i]>='a'&&s1[i]<='z')
s1[i]=s1[i]-'a'+'A';
if(s1[i]>='0'&&s1[i]<='9')
idx=s1[i]-'0';
else if(s1[i]>='A'&&s1[i]<='Z')
idx=s1[i]-'A'+20;
else
idx=50;
if(vis[idx]==0)
{
cout<<s1[i];
vis[idx]=1;
}
}
return 0;
}
【例】A1092 To Buy or Not to Buy (20 分)
bead n.珠子
string n. 线,弦,细绳;一串,一行
ATTENTION
- 先分别记录两个字符串出现过的字符以及出现的次数。
- 然后对于每一种珠子进行枚举,如果
e[i]>s[i]
,则说明结果为No
,且两数之差为miss
的数量;反之,结果为Yes
,且两数之差为要多买的数量。
#include <iostream>
#include <string>
using namespace std;
int e[100]={0}; //0~9 -'0' ; a~z -'a'+20 ; A~Z -'A'+50
int s[100]={0};
string shop,eva;
int main()
{
cin>>shop>>eva;
for(int i=0;i<shop.size();i++)
{
if(shop[i]>='0'&&shop[i]<='9')
s[shop[i]-'0']++;
else if(shop[i]>='a'&&shop[i]<='z')
s[shop[i]-'a'+20]++;
else
s[shop[i]-'A'+50]++;
}
for(int i=0;i<eva.size();i++)
{
if(eva[i]>='0'&&eva[i]<='9')
e[eva[i]-'0']++;
else if(eva[i]>='a'&&eva[i]<='z')
e[eva[i]-'a'+20]++;
else
e[eva[i]-'A'+50]++;
}
int ans=0,miss=0;
bool flag=true;
for(int i=0;i<100;i++)
{
if(s[i]>=e[i])
ans+=s[i]-e[i];
else
{
miss+=e[i]-s[i];
flag=false;
}
}
if(flag)
cout<<"Yes"<<" "<<ans;
else
cout<<"No"<<" "<<miss;
}
【例】A1041 Be Unique (20 分)
lottery n. 彩票;碰运气的事,难算计的事;抽彩给奖法
ATTENTION
- 在
vis
数组中记录下标数出现的次数。在num
数组中记录下标数第一次出现的序号。 - 遍历
vis
数组,对于仅出现过一次的数,查找num
数组,寻找最早出现的那个。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
int num[20000];
int vis[20000]={0};
int main()
{
scanf("%d",&n);
fill(num,num+20000,-1);
fill(vis,vis+20000,0);
for(int i=0;i<n;i++)
{
int tmp;
scanf("%d",&tmp);
if(vis[tmp]==0)
num[tmp]=i;
vis[tmp]++;
}
int idx=200000,ans=-1;
for(int i=1;i<10001;i++)
{
if(vis[i]==1&&num[i]<idx)
{
idx=num[i];
ans=i;
}
}
if(ans==-1)
printf("None");
else
printf("%d",ans);
}
【例】A1050 String Subtraction (20 分)
Subtraction n. [数] 减法;减少;差集
#include <iostream>
#include <string>
using namespace std;
int letter[300]={0};
string s1,s2,s;
int main()
{
//cin不能读入空格
getline(cin,s1);
getline(cin,s2);
for(int i=0;i<s2.size();i++)
{
int tmp=(int)s2[i];
letter[tmp]=-1;
}
for(int i=0;i<s1.size();i++)
{
int tmp=(int)s1[i];
if(letter[tmp]==0)
cout<<s1[i];
}
}
【例】A1048 Find Coins (25 分)
ATTENTION
- 一般大部分点都过了但少部分点没过的时候,就是有情况没有被考虑到,比如这道题,如果
i==m-i
是答案,那么就要确保至少有两个i
。 - 这道题也可以用two pointers做,先把所有的数从小到大排序(不能删去重复的),然后同时从左和从右开始寻找。
#include <iostream>
using namespace std;
int n,m;
int vis[1010]={0};
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
int tmp;
cin>>tmp;
vis[tmp]++;
}
bool flag=false;
for(int i=1;i<m;i++)
{
if(vis[i]!=0&&vis[m-i]!=0)
{
if(i==m-i&&vis[i]<2) continue;
cout<<i<<" "<<m-i;
flag=true;
break;
}
}
if(!flag)
cout<<"No Solution";
}
two pointers
#include <iostream>
#include <algorithm>
using namespace std;
int n,m;
int value[200000];
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>value[i];
sort(value,value+n);
bool flag=false;
int i=0,j=n-1;
while(i<j)
{
if(value[i]+value[j]==m)
{
cout<<value[i]<<" "<<value[j];
flag=true;
break;
}
else if(value[i]+value[j]>m)
j--;
else
i++;
}
if(!flag)
cout<<"No Solution";
}
【例】A1078 Hashing (25 分)
sequence n.序列
distinct a.有区别的
distinct positive integers 不重复的正整数
Quadratic a.二次的 n.二次方程式
probing n.探索
increment n.增量
collision n.碰撞,冲突
ATTENTION
- 取模!!!
- 结果其实可以直接输出,不用存在
vector
里。
#include <cstdio>
#include <vector>
#include <cmath>
using namespace std;
int mSize,n;
int num;
int hashList[10010]={0};
vector<int> ans;
bool isPrime(int n)
{
if(n<=1) return false;
int sqr=(int)sqrt(1.0*n);
for(int i=2;i<=sqr;i++)
if(n%i==0) return false;
return true;
}
int main()
{
scanf("%d %d",&mSize,&n);
while(!isPrime(mSize))
mSize++;
for(int i=0;i<n;i++)
{
scanf("%d",&num);
int pos;
bool flag=false;
for(int j=0;j<mSize;j++)
{
pos=(num+j*j)%mSize;
if(hashList[pos]==0)
{
hashList[pos]=num;
ans.push_back(pos);
flag=true;
break;
}
}
if(!flag) ans.push_back(-1);
}
for(int i=0;i<ans.size();i++)
{
if(i!=ans.size()-1)
{
if(ans[i]==-1)
printf("- ");
else
printf("%d ",ans[i]);
}
else
{
if(ans[i]==-1)
printf("-");
else
printf("%d",ans[i]);
}
}
return 0;
}
【例】A1145 Hashing - Average Search Time (25 分)
ATTENTION
- 平方探测(只有正方向的):pos=(H(key)+k2)%TSize,0<=k<TSize
- 如果根据平方探测,遇到一个为空的位置,说明该元素不在哈希表中,查找结束。如果遇到一个查找位置为空,也说明该元素不在哈希表中!
- 元素的查找次数=冲突次数+1
- 这道题再做还是觉得很奇怪…如果查遍了整个表,没有查到而且没有遇到空的,则不确定这个元素在不在表中,这时查找时间需要再+1(可能是与其他什么比较确定元素到底在不在表中?),但根据查找次数=冲突次数+1这个规则来看的确需要+1。不管怎样,先记住这个点吧…
#include <cstdio>
int msize,n,m;
int hash[10010]={0};
bool isPrime(int n) //素数问题
{
if(n<=1) return false;
for(int i=2;i*i<=n;i++)
if(n%i==0) return false;
return true;
}
int main()
{
scanf("%d %d %d",&msize,&n,&m);
while(isPrime(msize)==false)
msize++;
for(int i=1;i<=n;i++)
{
int num;
scanf("%d",&num);
bool flag=false;
for(int j=0;j<msize;j++)
{
int pos=(num+j*j)%msize;
if(hash[pos]==0)
{
hash[pos]=num;
flag=true;
break;
}
}
if(flag==false)
printf("%d cannot be inserted.\n",num);
}
int sum=0;
for(int i=0;i<m;i++)
{
int num;
scanf("%d",&num);
bool flag=false;
for(int j=0;j<msize;j++)
{
flag=false;
sum++;
int pos=(num+j*j)%msize;
if(hash[pos]==num)
{
flag=true;
break;
}
else if(hash[pos]==0)
{
flag=true;
break;
}
}
if(!flag) sum++;
}
printf("%.1f",sum*1.0/m);
}
4.4 贪心
【例】A1038 Recover the Smallest Number
ATTENTION
- 注意去
0
要在最后输出的时候去,并且只去除最开头的0
。不能在中间输入各个段的时候去0
,因为中间的0
和开头的0
不一样,中间的0
不能去掉。 - 本以为在排序后输出时,对于所有
==0
的string
都不输出就可以去掉所有的只含有0
的段,实则不然,字符串中的“0”
和“00”
和“000”
并不相等!!! - 注意去
0
后可能会出现输出为空的情况,这时候要输出“0”
。 - 太妙了这思路啊啊啊啊啊!!!!!
#include <cstdio>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
int n;
string str[20000];
bool cmp(string a,string b)
{
return a+b<b+a;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
string tmp;
cin>>tmp; //
str[i]=tmp;
}
sort(str,str+n,cmp);
bool flag=true;
for(int i=0;i<n;i++)
{
if(str[i]=="0"){}
else
{
while(flag&&str[i][0]=='0')
str[i].erase(0,1);
if(str[i].size()!=0)
flag=false;
cout<<str[i];
}
}
if(flag)
cout<<"0";
return 0;
}
改进:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int n;
string str[20000],s;
bool cmp(string a,string b){ return a+b<b+a; }
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>str[i];
sort(str,str+n,cmp);
for(int i=0;i<n;i++)
s+=str[i];
while(s[0]=='0')
s.erase(0,1);
if(s.length()==0)
cout<<"0";
cout<<s;
return 0;
}
【例】A1037 Magic Coupon (25 分)
coupons n.优惠券
bonus n.奖金;红利;额外津贴
ATTENTION
- 遇到这种不能套算法的题有点不敢做…
- 遇到不能对应算法的题目时,先自己思考一下~
- 这道题本质就是给两串正负都有的数量不一定相等的数序列A和B,求A和B中元素的最大乘积和。
- 注意还有0的情况,如果有一个乘数是0,那得到的也是0,不划算不划算。所以只计算最小的负数乘最小的负数和最大的正数乘最大的正数啦。
#include <cstdio>
#include <algorithm>
using namespace std;
int ans=0,nc,np;
int coupon[100010],value[100010];
int main()
{
scanf("%d",&nc);
for(int i=1;i<=nc;i++)
scanf("%d",&coupon[i]);
scanf("%d",&np);
for(int i=1;i<=np;i++)
scanf("%d",&value[i]);
sort(coupon+1,coupon+nc+1);
sort(value+1,value+np+1);
int cne=0,pne=0,cpo=0,ppo=0;
for(int i=1;i<=nc;i++)
{
if(coupon[i]<0)
cne++;
else
break;
}
for(int i=nc;i>=1;i--)
{
if(coupon[i]>0)
cpo++;
else
break;
}
for(int i=1;i<=np;i++)
{
if(value[i]<0)
pne++;
else
break;
}
for(int i=np;i>=1;i--)
{
if(value[i]>0)
ppo++;
else
break;
}
int n=min(cne,pne);
for(int i=1;i<=n;i++)
ans+=coupon[i]*value[i];
n=min(cpo,ppo);
for(int i=np,j=nc;i>np-n,j>nc-n;i--,j--)
ans+=coupon[j]*value[i];
printf("%d",ans);
return 0;
}
【例】A1067 Sort with Swap(0, i) (25 分)
ATTENTION
- 只能与0交换
- 需要注意到,要想要交换次数最少,那么最好的结果就是每次交换使得一个数能够回到它的位置。那么每次都拿
0
与本应在pos[0]
这个位置的数进行交换。 - 但是,如果在交换过程中,
0
不小心回到了0
处,就需要与一个没有在他的位置的数交换,虽然这是一次无效交换,但如果拿0
与已经回到位置的数进行交换,交换次数会更多。 - 如果在
0
到达0
处后遍历寻找与它交换的数,会超时,因为这个操作最坏时间复杂度为O(n2)。所以,应该在每次0
与其他数交换的时候,就维护一个数,使他存储着一个不在位置上的数。这样最欢时间复杂度也只是O(n)。 - 有时候,看似简单的操作,时间复杂度会很高,但有时候,看似麻烦的操作,时间复杂度很低,所以在遇到超时的点的时候,要仔细考虑。
- 贪心就是难啊哎…
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100010;
int n,pos[maxn]={0},diff[maxn]={0};
int cnt,res=0;
int main()
{
scanf("%d",&n);
int tmp;
for(int i=0;i<n;i++)
{
scanf("%d",&tmp);
pos[tmp]=i;
if(tmp!=i) cnt++;
}
int kmax=1;
while(cnt)
{
res++;
if(pos[0]!=0)
{
swap(pos[0],pos[pos[0]]); //交换 0 和 pos[0]
cnt--;
while(pos[kmax]==kmax) kmax++;
}
else if(pos[0]==0)
{
if(kmax>=n)
{
res--;
break;
}
else
swap(pos[0],pos[kmax]);
}
}
printf("%d",res);
}
【例】A1070 Mooncake (25 分)
filling n. 填充;填料
crust n.外壳;面包皮;坚硬外皮
amount n. 数量,数额;总数
inventory n. 存货,存货清单;详细目录
ATTENTION
- 题目里的价格是总价格不是单价。
- 不能用
while(d)
求收益,可能包含了超市需求大于月饼总数量的情况。 - 月饼的数量也得用
double
,注意题目里没有说明数量是整数!!!
#include <cstdio>
#include <algorithm>
using namespace std;
double sum=0;
int n,d;
struct Per{
double price,ptot,cnt;
}mc[1010];
bool cmp(Per a,Per b)
{
return a.price>b.price;
}
int main()
{
scanf("%d %d",&n,&d);
for(int i=0;i<n;i++)
{
scanf("%lf",&mc[i].cnt);
}
for(int i=0;i<n;i++)
{
scanf("%lf",&mc[i].ptot);
mc[i].price=mc[i].ptot/mc[i].cnt;
}
sort(mc,mc+n,cmp);
int idx=0;
while(idx<n)
{
if(mc[idx].cnt<=d)
{
d-=mc[idx].cnt;
sum+=mc[idx].ptot;
}
else if(mc[idx].cnt>d)
{
sum+=((double)(mc[idx].ptot*1.0)*(double)d/(double)mc[idx].cnt);
d=0;
}
if(d==0)
{
break;
}
idx++;
}
printf("%.2f",sum);
return 0;
}
【例】A1101 Quick Sort (25 分)
partition n.划分
typically v.通常
pivot n. 枢轴;中心点;中心
ATTENTION
- 暴力有两个点会超时,虽然只能拿20分,但是如果是在考场上,先把20分拿到手,有时间再找方法。
- 这道题有点像
two pointers
。既然对于每个点遍历一次判断其左边是否都小于它,其右边是否都大于它,那不如先遍历两遍整个数组,得到每个点左边的最大值和右边的最小值。注意首尾两个点要单独处理。 - 很棒的题目,学到了!
- 注意PAT中所有输出非单行的,都要加换行符。(这道题卡了一个点orz)
- 果然英文题还是很有欺骗性的嗯。
//#include <cstdio>
//#include <algorithm>
//using namespace std;
//
//int n,idx=0;
//int num[100010];
//int sorted[100010];
//int res[100010];
//
//int main()
//{
// scanf("%d",&n);
// for(int i=0;i<n;i++)
// {
// scanf("%d",&num[i]);
// sorted[i]=num[i];
// }
// sort(sorted,sorted+n);
// if(sorted[0]==num[0])
// res[idx++]=num[0];
// int max=num[0];
// for(int i=1;i<n;i++)
// {
// if(num[i]==sorted[i]&&max<num[i])
// res[idx++]=num[i];
// if(max<num[i])
// max=num[i];
// }
// printf("%d\n",idx);
// for(int i=0;i<idx;i++)
// {
// if(i==0)
// printf("%d",res[i]);
// else
// printf(" %d",res[i]);
// }
// printf("\n");
// return 0;
//}
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100010;
int n,cnt=0;
int num[maxn],leftMax[maxn],rightMin[maxn],ans[maxn];
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&num[i]);
fill(leftMax,leftMax+maxn,0);
fill(rightMin,rightMin+maxn,1e9+10);
int max=num[0],min=num[n-1];
for(int i=1;i<=n;i++)
{
leftMax[i]=max;
if(num[i]>max) max=num[i];
}
for(int i=n-2;i>=0;i--)
{
rightMin[i]=min;
if(num[i]<min) min=num[i];
}
if(rightMin[0]>num[0]) ans[cnt++]=num[0];
if(leftMax[n-1]<num[n-1]) ans[cnt++]=num[n-1];
for(int i=1;i<n-1;i++)
{
if(num[i]>leftMax[i]&&num[i]<rightMin[i])
ans[cnt++]=num[i];
}
sort(ans,ans+cnt);
printf("%d\n",cnt);
for(int i=0;i<cnt;i++)
{
if(i==0)
printf("%d",ans[i]);
else
printf(" %d",ans[i]);
}
printf("\n");
return 0;
}