……这个在线测试赛后不可练习,题目也看不了了,但是代码我都好好的留下来了
先上提交记录:
之后从前往后复述题意,写题解吧。
本文地址:http://blog.csdn.net/fcxxzux/article/details/51931463
Problem A - Alphabet Replacement
题意:给你2个长度相等的,由小写字母组成的字符串。现在允许你做一个操作:把2个字符串中出现的字符A换成字符B(允许自己换成自己,2个字符串里出现的字符A都要被替换),问有没有一种替换方案,使得替换后2个字符串完全一样?
比如,输入:
ababcd
babacd
输出Yes,因为可以通过把a换成b,使得2个字符串都变成bbbbcd。
——英文题面描述的题意有毒,当搞懂正确题意后,还是很白送的:暴力枚举所有替换方案,并检查
也就26*26*length,这里length还只有100,直接交。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> using namespace std; typedef long long ll; char sa[105],sb[105]; int len; char change(char x,char s,char g){ return x==s?g:x; } bool check(char src,char goal){ for(int i=0;i<len;i++){ if(change(sa[i],src,goal)!=change(sb[i],src,goal))return false; } return true; } int main(){ scanf("%s%s",sa,sb); len=strlen(sa); for(char a='a';a<='z';a++){ for(char b='a';b<='z';b++){ if(check(a,b)){ puts("Possible"); return 0; } } } puts("Impossible"); return 0; }
Problem B - Marathon Runners
2个人Takahashi和Aoki站在起跑线上。已知:
1、发令枪响后,Takahashi立刻出发,Aoki将延迟0.5秒出发
2、已知Takahashi跑一圈用a秒,Aoki跑一圈用b秒(a和b都是整数)
求:第k次有人冲过起点线的时候,是谁冲过的?
比如a=3 b=2 k=1,输出Aoki,a=3 b=2 k=2,输出Takahashi
从起点出发不算冲过起点线,之后2.5秒的时候Aoki冲过起点线,3秒的时候Takahashi才冲过起点线,下一次是Aoki在4.5秒冲过,再之后是Takahashi在6秒冲过,之后Aoki在6.5和8.5秒各自冲过一次,9秒的时候Takahashi再冲过。
做法:因为k实在不大(k<=100000),所以,与其写二分搜索伤害自己,不如,模拟。
采用事件触发的思路,记录每个人下次冲过起点线的时间(timer被触发的时刻),之后循环判断谁冲过起点线,并更新他下次冲过起点线的时间。
注意由于a,b<=100000,导致时刻超过32位整型的表示范围,请使用64位整型。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <queue> #include <map> using namespace std; typedef long long ll; template <class T> inline void scan(T &ret) { char c; ret=0; while((c=getchar())<'0'||c>'9'); while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar(); } long long a,b,k; int main(){ scan(a);scan(b);scan(k); a*=2;b*=2; priority_queue<pair<ll,int> > q; q.push(make_pair(-a,0)); q.push(make_pair(-b-1LL,1)); for(;k-->1;){ pair<ll,int> x=q.top();q.pop(); if(x.second==0){ x.first-=a; q.push(x); }else{ x.first-=b; q.push(x); } } pair<ll,int> x=q.top(); puts(x.second?"Aoki":"Takahashi"); return 0; }
Problem C - Domino Effect
给你一个r*c的矩阵(r,c<=50),以左上角为(1,1),向下为x增大方向,向右为y增大方向,每个单元里是2种多米诺骨牌:
R:向右倒
C:向下倒
输入中还有一种情况
?:表示不确定,可以是R或C的任意一种(保证输入中不超过10个?)
现在你需要把所有多米诺骨牌推倒。请问,最少推倒次数的期望是多少?
比如,输入
1 2
??
输出1.5000000000000000000
对??,一共4种可能
RR、RC、CR、CC
对RR、RC,推倒全部骨牌,只需要动手1次(推(1,1))
对CR、CC,推倒全部骨牌,必须动手2次
所以,最小推倒次数的期望是1.5。
因为输入中不超过10个?,要枚举的情况也就1024种,所以暴力枚举走起。
对每种恢复的情况,怎么求最小的推倒次数期望呢?
因为骨牌只能向下和向右,不会回头,所以,我们只要找一块不会有任何骨牌可以压到的骨牌去推就行了
比如,上来就先推(1,1),因为(1,1)没有上面的和左边的骨牌了
之后再找一块没有上面和左边的骨牌的去推,贪心推完,就是最少次数。
(代码实现中采用了从第1行扫到最后一行,每行从左向右扫,没倒就推的办法。想一想,为什么这样找到的骨牌一定满足条件?)
最后使用数学期望的定义,算一下,输出,完事。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> #include <map> #include <vector> using namespace std; typedef long long ll; int r,c; char mp[55][55]; bool fall[55][55]; struct Node{ int x,y; Node(){} Node(int a,int b):x(a),y(b){} void set(char c){ mp[x][y]=c; } }; vector<Node> qmark; int ans=0; void makef(int x,int y){ while(x<r&&y<c){ fall[x][y]=1; if(mp[x][y]=='R')++y; else ++x; } } void check(){ memset(fall,0,sizeof(fall)); int cnt=0; for(int i=0;i<r;i++){ for(int j=0;j<c;j++){ if(!fall[i][j]){ ++cnt; makef(i,j); } } } ans+=cnt; } void dfs(int dp){ if(dp==qmark.size()){ check(); return; } qmark[dp].set('R'); dfs(dp+1); qmark[dp].set('C'); dfs(dp+1); } int main(){ scanf("%d%d",&r,&c); for(int i=0;i<r;i++)scanf("%s",mp[i]); for(int i=0;i<r;i++){ for(int j=0;j<c;j++){ if(mp[i][j]=='?'){ qmark.push_back(Node(i,j)); } } } dfs(0); printf("%.15f\n",(double)ans/(1<<(qmark.size()))); return 0; }
Problem D - Balanced Parentheses
现在你有一个长度为n(n<=300000)的,残缺的括号序列(只由' ( ' 和 ' ) '组成),你需要通过插入字符,使得这个残缺的序列变成合法的括号序列
你可以在原始残缺序列的开始、末尾和任意2个字符之间加 ' ( ' 或者 ' ) ' ,一个位置可以插入多次,每个位置有不同的代价。
求变成合法的括号序列需要的最小代价。
比如:输入
4
)(()
1 7 2 8 5
输出:3
在最开头和两个(中间分别插入(和),得到:
( ) ( ) ( )
(加粗的字符为插入的字符)
插入代价为1+2=3,这是最小的方案。
下面留白,这个题有点意思,大家可以思考一下。
做法:
考虑如何进行括号匹配的检查。
从左到右,碰到左括号压栈,碰到右括号出栈
如果需要出栈的时候,栈里没有左括号,那就不是合法序列,直接返回false
如果整个串处理完了,栈里还有左括号,那也不是合法序列,返回false
现在要变成合法序列,就要考虑,这两种返回false的情况都应该避免。
先考虑解决第一种情况。
如果要出栈的时候没有左括号,那就——强行塞给他一个啊!
所以,从左到右,一个右括号找不到匹配的左括号的时候,在这个右括号前面的所有位置里,挑一个代价最小的位置,补上一个左括号。
(for dummies:挑一个代价最小的位置,这件事情怎么做才做的漂亮?
答:你看你现在要从左到右扫过去吧?那我可不可以同期维护一个变量,表示前面的东西的最小值?当我考虑的位置往后移的时候,最新的最小值=min(之前的最小值,新位置的值))
ok,现在考虑解决第二个问题——栈里残留的左括号。
考虑从右往左进行二次检查,
之前我们希望对每个要求出栈的 ) ,有残余的 ( 与之匹配
现在,我们希望对每个 ( ,要求有多余的 ) 与之匹配——而之前第一遍多出来的(是没有的
所以,类似的,从后往前扫描,) 入栈,( 出栈,出栈时没有对应括号的,在之前扫过的位置挑一个代价最小的地方补一个 ) 。
问题解决。
时间复杂度:正着扫一遍,倒着扫一遍,所以是O(n)的。
因为最坏情况下,一个位置的权值有10亿之大,最坏有300000括号要插入,所以,结果需要用64位整型表示。
#include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <math.h> #include <algorithm> using namespace std; typedef long long ll; template <class T> inline void scan(T &ret) { char c; ret=0; while((c=getchar())<'0'||c>'9'); while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar(); } inline void out(long long x) { if(x>9) out(x/10); putchar(x%10+'0'); } ll ans=0; char str[300005]; int weight[300005]; int main(){ int n; scanf("%d%s",&n,str); for(int i=0;i<=n;i++)scan(weight[i]); { int left=0; int minv=weight[0]; for(int i=0;i<n;i++){ if(str[i]=='(')++left; else if(str[i]==')')--left; if(left<0){ ans+=minv; left=0; str[i]=' '; } minv=min(minv,weight[i+1]); } } { int left=0; int minv=weight[n]; for(int i=n-1;i>=0;i--){ if(str[i]=='(')--left; else if(str[i]==')')++left; if(left<0){ ans+=minv; left=0; str[i]=' '; } minv=min(minv,weight[i]); } } out(ans); return 0; }