贪心,模拟
1
原题链接
题意: 给n个只包含’(‘,’)’的字符串 问怎样组合这些字符串可以获得最长平衡子序列(不必连续)的长度
思考过程及相关解法:可以先把每个串合法括号匹配数统计一下,再记录其不合法个数,此时的序列形式为”…)))((((…” 格式,可以对每个序列处理出一个有序对(a,b),a代表该序列左端右括号,b代表该序列右端左括号。然后对所有序列排序,之后模拟计算结果
排序的贪心思想并不会证明 见参考代码
参考代码:
#include
#include
#include
using namespace std;
const int MAXS = 100005;
struct node{
int l,r,cnt;
bool operator < (node const& b) const{
if (l < r && b.l >= b.r ) return true; //a的左端比右端短 b的左端比右端长 则a一定排前面
if (l >= r && b.l < b.r) return false;
//接下来考虑负贡献 a,b都是右端长 此时要看谁左端提供负贡献最小 然后把它放在前面 都是左端长同理
if (l < r && b.l < b.r) return l < b.l;
if (l >= r && b.l >= b.r) return r > b.r;
}
}a[MAXS];
char s[MAXS];
int main(){
int t;
scanf("%d",&t);
while (t--){
int n;
scanf("%d",&n);
for (int i = 1; i <= n; ++i){
scanf("%s",s);
int len = strlen(s);
a[i].l = a[i].r = a[i].cnt = 0;
//处理每个串中的合法匹配 得到每个串的不合法括号序列 一定是左边是右括号右边是左括号形式
for (int j = 0;j < len; ++j){
if (s[j] == '(') a[i].r++; //左边的是右括号 左括号在右边
else{
if (a[i].r > 0) {
a[i].r--; a[i].cnt++;
}else a[i].l++;
}
}
}
sort(a+1,a+n+1);
int ans = 0;
int lcnt = 0;
//模拟维护左括号数即可
for (int i = 1; i <= n; ++i){
if (a[i].l > lcnt) a[i].l = lcnt;
ans += a[i].l + a[i].cnt;
lcnt -= a[i].l;
lcnt += a[i].r;
}
printf("%d\n",ans * 2);
}
return 0;
}
原题链接
题意:对一个序列,有m行区间表示该区间内的所有数字都不同,问能得到的从1到n字典序最小的序列 输出这个序列
思考过程及相关解法:问题在于处理重叠区间的数值填数 因为必须要维护可使用的值的序列,且要维护这个序列每次都取最小值,可以考虑使用优先队列或者set 维护这个序列 处理区间时注意相同的区间端点,要保证处理到最长的另一端
参考代码:
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 1000005;
struct node{
int l,r;
}a[MAXN];
int ans[MAXN];
bool cmp(node a,node b){
if (a.l == b.l) return a.r < b.r;
return a.l < b.l;
}
int flag[MAXN];
priority_queue<int,vector<int>,greater<int> > que;
int main(){
int t;
while (scanf("%d",&t)!=EOF){
while (t--){
int n,m;
scanf("%d%d",&n,&m);
for (int i = 0;i < m; ++i){
scanf("%d%d",&a[i].l,&a[i].r);
}
sort(a,a+m,cmp);
for (int i = 1;i <= n; ++i){
que.push(i);
ans[i] = 0;
flag[i] = 0;
}
int pos = 1,k = 0;
for (int i = 1;i <= n; ++i){
//处理一个区间
while (k < m && a[k].l == i){
pos = max(i,pos);
while (pos <= a[k].r){
ans[pos] = que.top();
flag[ans[pos]] = 1;
que.pop();
pos++;
}
k++;
}//保证区间内的数不同 ans数组再往前走 用过的位置的数已经可以使用
//printf("*%d %d*\n",i,ans[i]);
if (ans[i] == 0) ans[i] = 1;
else if (flag[ans[i]] == 1){
que.push(ans[i]);
flag[ans[i]] = 0;
}
}
for (int i = 1;i <= n; ++i){
printf("%d%c",ans[i],i == n ? '\n' : ' ');
}
while (!que.empty()) que.pop();
}
}
return 0;
}
/*set维护 参考杜教代码
set<int> s;
for (int i = 1;i <= n; ++i) pre[i] = i,s.insert(i);
for (int i = 1;i <= m; ++i){scanf("%d%d",&l,&r); pre[r] = min(pre[r],l);}
//将区间由右至左的pre[i]更新为区间左端点
for (int i = n-1; i >= 1; ++i) pre[i] = min(pre[i],pre[i+1]);
int cnt = 1;
for (int i = 1;i <= n; ++i){
while (cnt < pre[i]){
//将前一段区间用过的加入set 维护最小值 (从cnt到该区间左端的值可以使用了 加入进去)
s.insert(ans[cnt]);
cnt++;
}
ans[i] = *s.begin();
s.erase(ans[i]);
}
*/
原题链接
题意:时区转换 将北京地区的时区时间转换为对应的时区的时间
思考过程及相关解法:直接模拟即可,将时间统一转化为最小精度(分钟)计算,注意对一天24*60min取模。时区可以是小数,所以处理起来可能不方便 这里看了杜教的代码,学习了一下sscanf的用法 大致就是将字符串从某位开始以需要的数据类型读取出来。 (在数据类型处可声明读取长度)
参考代码:
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 1000005;
int _;
int sgn,a,b,ans;
double d;
char s[30];
int main(){
for (scanf("%d",&_);_;_--){
scanf("%d%d%s",&a,&b,s);
ans = a*60+b;
sgn = s[3] == '+' ? 1 : -1;
sscanf(s+4,"%lf",&d);
int c = (int)(d * 10 + 0.1);
c = sgn * c * 6 - 8 * 60;
ans += c;
ans %= (24 * 60);
if (ans < 0) ans += (24 * 60);
printf("%02d:%02d\n",ans/60,ans%60);
}
return 0;
}