2014哈商大ICPC/ACM校赛解题报告


被debug邀请去参加校赛,哎,被虐。。我对不起工大。。

因为本人不搞ACM,算法处于HelloWorld水准。。

尽管题目除了鸟不拉屎星人之外都很水,但我能做到这个程度,完全是超水平发挥了。。

数据:点此下载

==============================================================


a:逆序数组+删除特定元素

题目:
小伙伴们好像很多没接触过ICPC,那先来一道水题尝尝鲜,给出
一个数组,和一个特征值。将这个数组中特征值值删除后逆序输出。
EG: 数组,1 2 3 4 5 6 7特征值4
那么输出是:
7 6 5 3 2 1

输入:
输入包含3行,第一行输入N(N< 100)表示数组长度
第二行输入 N个数
第三行 输入特征值T

输出:
删除特征值后的逆序数组,每组输出要换行。

样例输入:
7
1 2 3 4 5 6 7
4
6

样例输出:
765321

代码:
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3.   
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     int n,tmp,delNum;  
  9.   
  10.     while(cin >> n){  
  11.         vector<int> a;  
  12.         while(n--){  
  13.             cin >> tmp;  
  14.             a.push_back(tmp);  
  15.         }  
  16.         cin >> delNum;  
  17.   
  18.         vector<int>::reverse_iterator a_rIter;  
  19.         for(a_rIter = a.rbegin();a_rIter != a.rend(); ++a_rIter){  
  20.             if(*a_rIter == delNum)  
  21.                 continue;  
  22.             cout << *a_rIter;  
  23.         }  
  24.         cout << endl;  
  25.     }  
  26.     return 0;  
  27. }  



b:括号匹配


题目:
在公式中经常遇到大量的括号,但是这些括号匹配的对不对呢?
因为不允许一个括号中包含不成对的括号.
不同类型的括号可以互相包含比如这样{()},([]),[{}], {[][]()};
但是一种类型的括号不允许包含其他类型不成对的括号,比如这
样就是不允许的:{(} [{] {)};
)(,}{,][这样也是不允许的。
现在给出一个有括号组成的字符串,判断其是否合法。

输入:
每组输入包含一个括号字符串

输出:
判断括号是字符串是否合法,合法输出yes不合法输出no
每次输出要换行

样例输入:
{}{}()()(()){[()]}
{}{}(([])){[}]


样例输出:
Yes
No

代码:
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <stack>  
  3.   
  4. using namespace std;  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     stack<char> s;  
  10.     string str;  
  11.     while(cin >> str){  
  12.         char tmp;  
  13.         bool ans = true;  
  14.         for(unsigned int i = 0 ; i < str.size();i++){  
  15.             if(str[i] == '(' || str[i] == '[' || str[i] == '{')  
  16.                 s.push(str[i]);  
  17.             else{  
  18.                 if(s.empty()){ //如果栈为空,而且还遇到这种字符就直接No了  
  19.                     ans = false;  
  20.                     break;  
  21.                 }  
  22.                 tmp = s.top();  
  23.                 s.pop();  
  24.                 if((str[i] == ')' && tmp == '(') ||  
  25.                    (str[i] == ']' && tmp == '[') ||  
  26.                    (str[i] == '}' && tmp == '{')){  
  27.                     continue;  
  28.                 }else{  
  29.                     ans = false;  
  30.                     break;  
  31.                 }  
  32.             }  
  33.         }  
  34.         if(ans)  
  35.             cout<<"Yes"<<endl;  
  36.         else  
  37.             cout<<"No"<<endl;  
  38.     }  
  39.     return 0;  
  40. }  


c: 再多一天


题目:
输入一个日期,你来算算第二天的日期

输入:
每组输入包含一个日期,格式如 2014-4-5

输出:
输出第二天的日期 2014-4-6;每次输出要换行;

样例输入:
2014-4-5
2014-4-6

样例输出
2014-4-6
2014-4-7

代码:
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <string>  
  3. #include <vector>  
  4. #include "stdio.h"  
  5.   
  6. using namespace std;  
  7.   
  8. class Time{  
  9. public:  
  10.     int year;  
  11.     int month;  
  12.     int day;  
  13.   
  14.     Time(int year,int month,int day)  
  15.     {  
  16.         this->day = day;  
  17.         this->month = month;  
  18.         this->year = year;  
  19.     }  
  20.   
  21.     bool isRunYear(){  
  22.         if((this->year % 4 == 0 && this->year % 100 != 0) || this->year % 400 == 0)  
  23.             return true;  
  24.         return false;  
  25.     }  
  26.   
  27.     void addOneDay(){  
  28.         this->day += 1;  
  29.   
  30.         //do with day  
  31.         if(this->day == 32){  
  32.             this->day = 1;  
  33.             this->month += 1;  
  34.         }  
  35.         else if(this->day == 31 && (this->month == 4 || this->month == 6 || this->month == 9 || this->month == 11)){  
  36.             this->day = 1;  
  37.             this->month += 1;  
  38.         }  
  39.         else if(this->day == 30 && this->month == 2 && isRunYear()){  
  40.             this->day = 1;  
  41.             this->month += 1;  
  42.         }  
  43.         else if(this->day == 29 && this->month == 2 && !isRunYear()){  
  44.             this->day = 1;  
  45.             this->month += 1;  
  46.         }  
  47.   
  48.         //do with month  
  49.         if(month == 13){  
  50.             this->month = 1;  
  51.             this->year += 1;  
  52.         }  
  53.     }  
  54.   
  55. };  
  56.   
  57. int main()  
  58. {  
  59.     int y,m,d;  
  60.     while(scanf("%d-%d-%d",&y,&m,&d)==3){  
  61.         Time *t = new Time(y,m,d);  
  62.         t->addOneDay();  
  63.         cout<<t->year<<"-"<<t->month<<"-"<<t->day<<endl;  
  64.     }  
  65.     return 0;  
  66. }  

/*修改于2014-4-23:9:23
强子说,判断一个月是不是大月,直接if(this->day == 32) 就好了。。
太有道理了,不然就冗余了。。感谢强子的修正!
*/


一开始傻逼了,我对输入的“2014-1-1”,当成字符串,然后分割。。
附加赠送C++切割字符串,其中vector返回的是year,month,day的int型集合体:
[cpp]  view plain copy
  1. vector<int> split(string str, string patternStr)  
  2. {  
  3.     vector<int> res;  
  4.     for(unsigned int i = 0 ; i < str.size() ; i++){  
  5.         int pos = str.find(patternStr,i);  
  6.         string s;  
  7.         if(pos < str.size() && pos >= 0){  
  8.             s = str.substr(i,pos-i);  
  9.             i = pos;  
  10.         }else{  
  11.             s = str.substr(i);  
  12.         }  
  13.         stringstream ss; //string 转 int  
  14.         ss << s;  
  15.         int t;  
  16.         ss >> t;  
  17.         res.push_back(t);  
  18.     }  
  19.     return res;  
  20. }  


d:相似字符串

题目:
现在字符串相似有个法则:如果字符串A能通过有次的变换能
和字符串B相同那么说明他们是相似的。
这种变换法则是:将串头字母插入到串尾,然后整体迁移一位。
比如:abcde cdeab
abcde 通过将 a 置尾变成 bcdea
bcdea 通过将 b 置尾变成 cdeab
那么说明abcde 和 cdeab 是相似的。

输入:
输入包含两个等长字符串

输出:
若两字符串相似输出yes否则输出no

样例输入:
abcde cdeab
abc acb

样例输出:
Yes
No

代码:
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <string>  
  3.   
  4. using namespace std;  
  5.   
  6.   
  7. int main()  
  8. {  
  9.     string str1,str2;  
  10.     while(cin>>str1>>str2){  
  11.         string str3 = str2+str2;  
  12.         int length = str1.size();  
  13.   
  14.         if(str3.find(str1)<length)  
  15.             cout<<"Yes"<<endl;  
  16.         else  
  17.             cout<<"No"<<endl;  
  18.     }  
  19.     return 0;  
  20. }  

在str2+str2中如何能找到str1,就是相似。。
string.find() 找不到后会返回一个很大的值。。不是-1。。

 
/*修改于2014-4-28
C++的find()找不到的时候到底返回什么,有的说是-1,有的说是无穷大。
str.find("e")返回值是string::size_type。因为 string::size_type (由字符串配置器 allocator 定义) 描述的是 size,故需为无符号整数型别。
因为缺省配置器以型别 size_t 作为 size_type,于是 -1 被转换为无符号整数型别。
找不时,返回值是string::npos。 
npos是这样定义的: static const size_type npos = -1;
但输出是做无符号整数型别输出,即-1的补码4294967295。

所以应该这么用:
当 str.find("哦")==string::npos时则说明字符串str中不存在“哦”这个字符,
反之,str.find("哦")!=string::npos则说明字符串str中存在“哦”这个字符

用string:npos来判断即可。
*/

e:迷宫


题目:
小伙伴们钻进迷宫寻找宝藏,你能否从(左边)入口开始进去,寻找到
宝物呢。

输入:
给出一个迷宫矩阵图,0表示通过,1表示墙壁。有左上角进入迷
宫开始寻宝。

输出:
输出寻找到的宝物符号,每次输出要换行

Sample In:
1111111111111111111111
0000000000000000000001
1111111111111111111101
1111111111111111111101
11000000000000*1111101
1111111111111111111101
1111111111111111111101
1+11111111111111111101
1000000000000000000001
1111111111111111111111

Sample Out:
+


代码:
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include "stdio.h"  
  3.   
  4. using namespace std;  
  5.   
  6. char m[100][100];  
  7.   
  8. int r = 0; //row  
  9. int c = 0; //column  
  10.   
  11. void dfs(int x,int y)  
  12. {  
  13.     if(m[x][y] != '0')  
  14.         cout<<m[x][y]<<endl;  
  15.     m[x][y] = '1';//标记访问过  
  16.     //down  
  17.     if(x+1 < r && m[x+1][y] != '1'){  
  18.         dfs(x+1,y);  
  19.     }  
  20.     //up  
  21.     if(x-1 >= 0 && m[x-1][y] != '1'){  
  22.         dfs(x-1,y);  
  23.     }  
  24.     //right  
  25.     if(y+1 < c && m[x][y+1] != '1'){  
  26.         dfs(x,y+1);  
  27.     }  
  28.     //left  
  29.     if(y-1 >= 0 && m[x][y-1] != '1'){  
  30.         dfs(x,y-1);  
  31.     }  
  32.   
  33. }  
  34.   
  35. int main()  
  36. {  
  37.     //freopen("in.txt","r",stdin);  
  38.     //freopen("out.txt","w",stdout);  
  39.   
  40.     string t;  
  41.     int index = 0 ;  
  42.     while(cin>>t){  
  43.         for(unsigned int i=0;i<t.size();i++)  
  44.             m[index][i] = t[i];  
  45.         c = t.size();  
  46.         index ++;  
  47.     }  
  48.     r = index;  
  49.   
  50.     //入口在左边,找入口  
  51.     for(int i = 0;i<r;i++){  
  52.         if(m[i][0] == '0')  
  53.             dfs(i,0);  
  54.     }  
  55.   
  56.   
  57.     return 0;  
  58. }  



f:小胖子厨师


题目:
大家都知道小胖子是个吃货,但是都不知道小胖子还是个很厉害
的厨师,他的拿手功夫是切墩><.
有一天他切肉时陷入了沉思:我怎么才能用最少的刀数把肉切的
更碎呢(咱们先不考虑3维问题,只在平面上思考吧) 。
比如,我一刀下去能切成两块,两刀下去能切成四块,小胖子一
气之下切了N多刀,肉最多被切成了多少块(估计不能吃了) ?

In:
输入小胖子切了多少刀N

Out:
输出肉最多能被切成多少块,每次输出要换行

Sample in:
1
2

Sample out:
2
4

代码: 
[cpp]  view plain copy
  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     int n;  
  8.     while(cin>>n){  
  9.         int ans = (1+n)*n/2 + 1;  
  10.         cout<<ans<<endl;  
  11.     }  
  12.     return 0;  
  13. }  

这道题不得不停下来吐槽下,平面分割问题。。。

当有n-1条直线时,平面最多被分成了f(n-1)个区域。

则第n条直线要是切成的区域数最多,就必须与每条直线相交且不能有同一交点。

这样就会得到n-1个交点。

这些交点将第n条直线分为2条射线和n-2条线断。

而每条射线和线断将以有的区域一分为二。

这样就多出了2+(n-2)个区域。

故:
f(n)=f(n-1)+n
      =f(n-2)+(n-1)+n
       ……
      =f(1)+1+2+……+n
      =n(n+1)/2+1

g:小气的自来水公司


题目:
现在有N个村庄,村庄与村庄之间有自来水管联通,现在自来水公
司为了节省耗材将拆除多余耗材, 为了使拆卸得到的耗材最多并且要
保证村庄之间水路想通。

输入:
首先输入m n, (m,n< 26)m表示有m个村庄,n表示村庄之间有多
少水路。
接下来有n行,每行包含 两个大写字符,和一个整形数
Eg.AB 10,表示村庄A B之间存在水路且长度是10

输出:
拆卸结束后,村庄之间水路的总长度。

Sample in:
3 2
AB 1
B C 1

Sample out:
2

代码:
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <algorithm>  
  3. #include <queue>  
  4. #include <vector>  
  5. #include "stdio.h"  
  6.   
  7. using namespace std;  
  8.   
  9. #define MAXN 1000  
  10. #define INF 1<<30  
  11.   
  12. int closest[MAXN],lowcost[MAXN],m;// m为节点的个数  
  13. int G[MAXN][MAXN];//邻接矩阵  
  14.   
  15. int prim(int m)  
  16. {  
  17.     for(int i=0;i<m;i++)  
  18.     {  
  19.         lowcost[i]=INF;  
  20.     }  
  21.     for(int i=0;i<m;i++)  
  22.     {  
  23.         closest[i]=0;  
  24.     }  
  25.   
  26.   
  27.     closest[0]=-1;//加入第一个点,-1表示该点在集合U中,否则在集合V中  
  28.     int num=0,ans=0,e=0;//e为最新加入集合的点  
  29.     while(num<m-1)//加入m-1条边  
  30.     {  
  31.         int micost=INF,miedge=-1;  
  32.         bool flag = false;  
  33.         for(int i=0;i<m;i++){ //找出最小边  
  34.             if(closest[i]!=-1)  
  35.             {  
  36.                 int temp=G[e][i];  
  37.                 if(temp<lowcost[i])  
  38.                 {  
  39.                     lowcost[i]=temp;  
  40.                     closest[i]=e;  
  41.                 }  
  42.                 if(lowcost[i]<micost){  
  43.                     micost=lowcost[i];  
  44.                     miedge = i;  
  45.                     flag = true;  
  46.                 }  
  47.             }  
  48.         }  
  49.         if(!flag){  
  50.             return -1;  
  51.         }  
  52.         ans+=micost;  
  53.         closest[miedge]=-1;  
  54.         e = miedge;  
  55.         num++;  
  56.     }  
  57.   
  58.     return ans;  
  59. }  
  60.   
  61. int main()  
  62. {  
  63.     int m,n;  
  64.     while(scanf("%d %d",&m,&n) == 2){  
  65.         for(int i=0;i<m;i++) //清空数组  
  66.             for(int j=0;j<m;j++)  
  67.                 G[i][j] = INF;  
  68.   
  69.         char a,b;  
  70.         int w;  
  71.         for(int i = 0 ; i < n;i++){  
  72.             cin>>a>>b>>w;  
  73.             G[a-'A'][b-'A'] = w;  
  74.             G[b-'A'][a-'A'] = w;  
  75.         }  
  76.   
  77.         int ans = prim(m);  
  78.         if(ans == -1)  
  79.             cout<<"Can't do it"<<endl;  
  80.         else  
  81.             cout<<"Total:"<<ans<<endl;  
  82.     }  
  83.     return 0;  
  84. }  




h:吐槽星人大战鸟不拉屎大王


题目:
HUC要举办ACM/ICPC校赛的消息已经传遍了整个宇宙,位于
M77星云的鸟不拉屎大王为了赢得这次比赛, 他决定派出自己最为自
傲的队伍。
而在宇宙的深处,宇宙间的和平使者,总是破坏鸟不拉屎大王毁
灭宇宙大阴谋,继呕吐曼之后成为全宇宙大英雄的吐槽星人,在得知
了这个消息之后,为了阻止鸟不拉屎大王,也决定选出相应的队伍去
参加比赛。 (他们总是这样,无论鸟不拉屎大王在做什么,阻止已经
成为了习惯)但是宇宙间总是充斥着各种邪恶的存在,吐槽星人当然
不可能将全部的力量放在鸟不拉屎大王身上 (比如说位于古老地球的
蛇精,女王大人,在俘获了福禄小金刚之后也开展了妄图统治宇宙的
邪恶计划)所以吐槽星人在得知了鸟不拉屎大王派出的队伍之后, 也
派出了队伍。
在出发比赛的当天,由于星际虫洞发生了严重的交通事故,造成
了交通大阻塞。鸟不拉屎大王的队伍就和他们宿世的仇敌,吐槽星人
的队伍坐在了同一辆星际飞船上开始了旅行 (不要问我他们为什么没
有自己的旅行飞船, 要知道就算鸟不拉屎大王这种宇宙高富帅的存在
也会与资金紧张的时候,恰恰吐槽星人也受到了同样的困扰) 。我们
知道每个吐槽星人都有自己生而拥有的吐槽之力, 而鸟不拉屎大王的
手下也有属于自己的翔之力 (能力的数值都在1 ~ n ^ 2之间的整数) 。
现在正在飞往比赛地点的路程上发现了翔之力的秘密。 (这个是个大
消息)他们决定展开自己的秘密计划,具体的内容就是当时在飞船之
中,鸟不拉屎大王的手下坐成一排编号为0~p,吐槽星人坐一排编
号为0~q,两排平行,吐槽星人在位置不变的情况下同时发动吐槽
之力,各自能力会形成各自的一道能量波,能量波不能互相交叉, 发
向对面的鸟不拉屎大王的手下, 这样就可以将对方成功吸收成为自己
的人,为比赛加大胜利的筹码。我们会给出双方对应的翔之力和吐槽
之力,你要计算出可以吸收的最大人数,当然由于基因的不同,每个
人的能力不可能呈现出相同的大小。 而能够成功计算出最大吸收人数
的会有幸成为幸得吐槽星人, 维护宇宙的和平, 真是个有意义的工作。
比如这两支队伍的能力分别是
1 7 5 4 8 3 9
1 4 3 5 6 2 8 9
我们可以选择1 -> 4 ->8 -> 9 或者 1 -> 5 -> 8 -> 9 或者 1 -> 4 -> 3
-> 9 这三种吸收模式
而1->5->3->9这种模式就不可以

INPUT:
输入的第一行为数据组数Case (Case <= 10),接下来每组数据包含3
行,第一行为3个整数n,p,q ( 2 <= n <= 250, 0 <= q, p <= n ^ 2 ),
接下来第二行是鸟不拉屎大王的手下的能量值,都是1 ~ n^2之间的
整数,第三行是吐槽星人,格式同上。

OUTPUT:
对于每组数据,输出“Case x: y” ,x从1开始一直到Case,而y表示
吸收的最大人数。

sample input:
2
3 6 7
1 7 5 4 8 3 9
1 4 3 5 6 2 8 9
4 15 5
10 16 3 0 1 8 14 15 13 6 9 5 7 12 11 2
5 15 11 7 2 0

sample output:
Case 1: 4
Case 2: 3

/*修改于2014-4-28

这道题用到了:偏序集的Dilworth定理!

例子如下:

求一个序列的最长上升子序列。其最大值即等于不上升子序列的最小划分数。
这就涉及到组合数学中偏序集的 Dilworth定理,代码:

[cpp]  view plain copy
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4.   
  5. using namespace std;  
  6.   
  7. int a[100],f[100],g[100];  
  8.   
  9. int main(){  
  10.     freopen("lmis.in","r",stdin);  
  11.     freopen("lmis.out","w",stdout);  
  12.     int n,i;  
  13.     scanf("%d",&n);  
  14.     memset(g,0x7f,sizeof(g));  
  15.     memset(f,0,sizeof(f));  
  16.     for(i=0;i<n;i++)  
  17.       scanf("%d",&a[i++]);  
  18.   
  19.     for(i=0;i<n;i++){  
  20.             int k=lower_bound(g+1,g+n,a[i])-g;  
  21.             f[i]=k+1;  
  22.             g[k+1]=a[i];  
  23.     }  
  24.   
  25.     printf("%d\n",*max_element(f,f+n));  
  26.     return 0;  
  27. }  


*/


代码:

[cpp]  view plain copy
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. using namespace std;  
  5. const int MAXN=250*250+10;  
  6. const int INF=9999999;  
  7. int b[MAXN],n,p,q;  
  8. int g[MAXN],id[MAXN],x[MAXN];//g[i]:ICS为i时候的最小下标,x[i]:lis值  
  9. int main()  
  10. {  
  11.     //freopen("1.txt","r",stdin);  
  12.     int T,kase=1;  
  13.     while(scanf("%d",&T)==1){  
  14.         kase=1;  
  15.         while(T--)  
  16.         {  
  17.             int temp,len=0;  
  18.   
  19.             scanf("%d%d%d",&n,&p,&q);  
  20.             memset(id,0,sizeof(id));  
  21.             for(int i=1;i<=p+1;i++)  
  22.             {  
  23.                 scanf("%d",&temp);  
  24.                 id[temp]=i;  
  25.             }  
  26.             for(int i=0;i<q+1;i++)  
  27.             {  
  28.                 scanf("%d",&temp);  
  29.                 if(id[temp])  
  30.                     b[len++]=id[temp];  
  31.             }  
  32.   
  33.             for(int i=0;i<=len;i++)  
  34.                 g[i]=INF;  
  35.                   
  36.             int ans=0;  
  37.             for(int i=0;i<len;i++)  
  38.             {  
  39.                 int k=lower_bound(g+1,g+len+1,b[i])-g;  
  40.                 x[i]=k;  
  41.                 g[k]=b[i];  
  42.                 ans=max(ans,x[i]);  
  43.             }  
  44.             printf("Case %d: %d\n",kase++,ans);  
  45.   
  46.         }  
  47.     }  
  48.     return 0;  
  49. }  

你可能感兴趣的:(算法,ACM)