算法竞赛 第五章

例5-1 大理石在哪儿 

  • 对N个大理石进行排序,可用sort()进行排序。排序对象可以存在普通数组里(用sort(a,a+n)调用,也可以存在vector中(用sort(v.begin(),v.end())方式调用)。
  • 查找是否有大理石上写有某个数字。可用lower_bound(first,last,x)。与要查找的数做比较看是否相等。
  • lower_bound 返回的是内存地址,如果转换成数组索引的话需要lower_bound(first,last,x) - a
#include<cstdio>

#include<algorithm>

using namespace std;

const int maxn = 10000;



int main(){

int n,q,x,a[maxn],i,kase=0;

while(scanf("%d %d", &n, &q) == 2 && n){  //接收输入的格式

printf("CASE# %d:\n",++kase);

for(i = 0; i < n; i++){

scanf("%d", &a[i]);

}

sort(a,a+n);    //对大理石进行排序,以便利用算法查找是否存在某个数字

while(q--){

scanf("%d",&x);

int p = lower_bound(a,a+n,x) - a;  //利用二分法查找第一个大于等于x的数的地址

if( a[p] == x) printf("%d found at %d\n" ,x,p+1);        //大理石编号1-n,数组索引从0开始。

else printf("%d not found\n" ,x);

}

}

return 0;        

}

 

例5-2 木块问题  vector用法

  • 模拟4中操作,经过分析这四种操作可以抽象出来在不加任何限制条件的情况下有两个动作
    • 将某木块上方的所有木块全部归位。
    • 将第p堆高度为h及其上方的木块整体移动到p2堆的顶部(a上没有东西时,a摞在b上;a上有东西时,a及其上边的摞在b上)
  • 然后分析如果想做上述两个动作,还需要提前找到a木块的位置。 此时可以遍历查找
  • 因为每一摞木块的高度不一样,所以可以使用不定长数组vector.需要引入头文件。
  • 不定长数组常用法。
    • 声明vector               vector<int> a[maxn];
    • 读取vector的大小     a.size()
    • 改变vector的大小     a.resize()
    • 向尾部添加元素        a.push_back()
    • 删除最后一个元素     a.pop_back()
  • 分析move onto over pile的作用
    • move       onto    a,b归位
    • move       over    a归位
    • pile          onto    b归位
    • 都有move的时候,a归位
    • 都有onto的时候,b归位
#include<cstdio>

#include<vector>

#include<string>

#include<iostream>

using namespace std;



const int maxn = 30;

int n;

vector<int> pile[maxn];



//找到木块a所在的pile和height,并以引用的形式返回给调用者

void find_block(int a, int& p, int& h){

for(p = 0; p < n; p++){

for(h = 0; h < pile[p].size(); h++){

if(a == pile[p][h]) return;

}

}

}



//将木块a上的所有木块全部归位。

void put_back(int p, int h){

int i,b;

for(i = h + 1; i < pile[p].size(); i++){

b = pile[p][i];

pile[b].push_back(b);        //将木块放回原位

}

pile[p].resize(h+1);        //最后需要用到h所以前边应该将h的值赋给i,以免h改动。

}



//将第p堆高度为h及其上方的木块整体移动到p2堆的顶部

void put_into(int p1, int h, int p2){

int i;

for( i = h; i< pile[p1].size(); i++) {

int b = pile[p1][i];

pile[p2].push_back(b);

}

pile[p1].resize(h);

}



//输出

void print(){

int i;

for(i = 0; i < n; i++){

printf("%d:", i);

int j;

for(j = 0; j < pile[i].size(); j++)  printf(" %d",pile[i][j]);

printf("\n");

}

}

int main(){

int a,b,i;

cin >> n;

string s1,s2;

for(i = 0; i<n; i++) pile[i].push_back(i);        //对每一摞进行编号。

//模拟操作

//while(cin >> s1 >> a >> s2 >> b){    //这样的输入方式提交会显示runtime error

while((cin >> s1) && s1 != "quit"){

cin >> a >> s2 >> b;

int pa,ha,pb,hb;

find_block(a,pa,ha);

find_block(b,pb,hb);

if(pa == pb)  continue;

if(s1 == "move") put_back(pa,ha);  //string可以用==进行比较

if(s2 == "onto") put_back(pb,hb);

put_into(pa,ha,pb);

}

print();

return 0;

}

 

5-3 安迪的第一个字典 set集合

  • set(集合)用法:每个元素最多只出现一次,且set中的元素已经从小到大已经排好序。
  • 找出所有的不同单词,按字典序从小到大输出。所以要用set。for循环即可从小到大遍历所有元素。
  • stringstream常用来转换数据类型。
  • c++ 中的string类型
    • 与char*比较string类不用考虑内存是否足够、字符串长度等等。
    • C++中的string类是一个泛型类,由模板而实例化的一个标准类,本质上不是一个标准数据类型。
    • string的初始化  #include<string>

==、>、<、>=、<=、和!=比较字符串,可以用+或者+=操作符连接两个字符串,并且可以用[]获取特定的字符(类似数组)

  • c++迭代器(iterator)
#include<iostream>

#include<string>

#include<set>

#include<sstream>

using namespace std;



set<string> dict;        //string集合



int main(){

string s,buf;

while(cin >> s){

int i;

for(i = 0; i < s.length(); i++){

if(isalpha(s[i])) s[i] = tolower(s[i]);

else s[i] = ' ';

}

stringstream ss(s);

while(ss >> buf){

 dict.insert(buf);

}

}

set<string>::iterator it;

for(it = dict.begin(); it != dict.end(); ++it)

cout << *it << "\n";

return 0;

}

 

5-4 反片语  map映射

  • 输入一些单词,输出不能通过字符重排得到输入文本中的另外一个单词的单词。
  • 即两个单词等价的条件是字母相同(不区分大小写)
  • 为了比较两个单词是否等价,我们可以对单词进行标准化,即不改变单个字母本身,使其其他方面趋于一致化。这里我们将每个单词的字母先转化为小写,再按字典序从小到大对单词中的字母进行排序。
  • 可以使用map<string,int>记录每种标准化的单词出现的次数,如果只出现一次则应输出。
  • 但是最后输出时还需要保留输入中的大小写,并按字典序排列。所以应该将每次判断后符合条件的输出记录在vector<string>中,全部判断完后用sort进行排序,然后for循环遍历输出。
#include<iostream>

#include<string>

#include<cctype> //tolower()的头文件

#include<map>

#include<vector>

#include<algorithm> //sort()的头文件

using namespace std;



map<string,int> cnt;

vector<string> words;        //记录输入的单词



//将单词s进行“标准化”

string repr(string s) {

string ans = s;

int i;

for(i = 0; i < ans.length(); i++){

ans[i] = tolower(ans[i]);

}

sort(ans.begin(),ans.end());

return ans;

}



int main(){

int n = 0;

string s;

while(cin >> s){

if(s[0] == '#') break;

words.push_back(s);

string r = repr(s); //这里的r和s不相同吗?

if(!cnt.count(r))  cnt[r] = 0;

cnt[r]++;

}

vector<string> ans;        //记录满足条件的原始单词

int i;

for(i = 0; i < words.size(); i++){

if(cnt[repr(words[i])] == 1)  ans.push_back(words[i]);   //判断时不区分大小写,输出时按原始大小写输出

}

//按字典序排序后输出

sort(ans.begin(),ans.end());

for(i = 0; i < ans.size(); i++)

cout << ans[i] << "\n";

return 0;
}


 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值