Codeforces Beta Round #3 练习
A:裸的广搜题,需要输出路径
B:
贪心
抓住题目的特殊性,每个物品只有1 2 两种体积
先按性价比排序,贪心的优先选择性价比高的,某次选了之后体积超了,就剪掉
这样子遍历一遍之后还不是答案
因为可能会有1体积的空位,而可能通过去掉已选集合中某个1体积的物品,再用一个2体积的物品替代达到更优解
所以这里要判断一下
View Code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 100010; struct node { int type; int id; int value; }in[maxn]; int ret[maxn]; int cmp(node a,node b){ return a.value*1.0/a.type>b.value*1.0/b.type; } bool vis[maxn]; int main() { int n,V,i,j,tot=0; scanf("%d%d",&n,&V); int sum=0,ans=0; for(i=0;i<n;i++) { scanf("%d%d",&in[i].type,&in[i].value); in[i].id=i; sum+=in[i].type; ans+=in[i].value; } if(sum<=V) { printf("%d\n",ans); for(i=1;i<=n;i++) printf("%d ",i); return 0; } sort(in,in+n,cmp); sum=0;ans=0; int mi=-1,mx=-1,mi_id,mx_id; int f=0; for(i=0;i<n;i++) { int flag=1; vis[in[i].id]=true; sum+=in[i].type; ans+=in[i].value; if(sum>V) { flag=0; vis[in[i].id]=false; sum-=in[i].type; ans-=in[i].value; if(!f) { if(in[i].type==2) { f=1; mx=in[i].value; mx_id=in[i].id; } } } if(flag) { if(in[i].type==1) { mi=in[i].value; mi_id=in[i].id; } } } if(sum==V || mi==-1 || mx==-1) { printf("%d\n",ans); for(i=0;i<n;i++) { if(vis[i]) printf("%d ",i+1); } puts(""); } else { printf("%d\n",ans-mi+mx); vis[mi_id]=false; vis[mx_id]=true; for(i=0;i<n;i++) { if(vis[i]) printf("%d ",i+1); } puts(""); } return 0; }
C:
模拟题
3*3的棋盘,两个人轮流走,走成三个子连成一条线就赢了
判断当前状态合不合法,合法的话判断谁赢了,如果没人赢,判断谁是下一个走的人
感觉要注意很多细节,所以我写的有点烦
View Code
#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> char g[5][5]; bool ill=false; bool ok(char X) { int i,j,vis; int cnt=0; for(i=1;i<=3;i++) { vis=0; for(j=1;j<=3;j++) if(g[j][i]!=X) vis=1; if(!vis) cnt++; vis=0; for(j=1;j<=3;j++) if(g[i][j]!=X) vis=1; if(!vis) cnt++; } vis=0; for(i=1;i<=3;i++) if(g[i][i]!=X) vis=1; if(!vis) cnt++; vis=0; for(i=1;i<=3;i++) if(g[i][3-i+1]!=X) vis=1; if(!vis) cnt++; if(cnt==0) return false; int m=0,h=0; for(i=1;i<=3;i++) { for(j=1;j<=3;j++) { if(g[i][j]=='X') m++; if(g[i][j]=='0') h++; } } if(X=='0'&&m>h) {ill=true;return false; } if(X=='X'&&m==h){ill=true;return false; } return true; } int judge() { bool flag1=ok('X'); bool flag2=ok('0'); if(flag1 && flag2 || ill) return -1; if(flag1) return 1; if(flag2) return 2; if(!flag1 && !flag2) { int cnt=0; for(int i=1;i<=3;i++) { for(int j=1;j<=3;j++) { if(g[i][j]=='X' || g[i][j]=='0') { cnt++; } } } if(cnt==9) return 3; return 0; } } int main() { int i,j; for(i=1;i<=3;i++) { scanf("%s",g[i]+1); } int X=0,h=0; for(i=1;i<=3;i++) { for(j=1;j<=3;j++) { if(g[i][j]=='X') X++; if(g[i][j]=='0') h++; } } int flag=judge(); if(abs(X-h)>1 || h>X || flag==-1) { puts("illegal"); return 0; } if(flag!=0) { if(flag==1) puts("the first player won"); if(flag==2) puts("the second player won"); if(flag==3) puts("draw"); return 0; } if(X>h) puts("second"); else puts("first"); return 0; }
D:
上面三题都是练手,这题才是成长题
从左往右遍历,把问号变成右括号(并把这个位置的信息插入一个set),
如果发现加进某个右括号之后当前右括号的数量大于左括号的数量,
则前面肯定要有一个右括号变成左括号,就选取增加的花费最小的那个右括号变成左括号
也可以这样想:
把所有的问号全变成')'
然后再把一定数量(确定的数量)的右括号变成左括号
从左往右扫描,当发现扫到某个由问号转变而来的右括号时,判断一下当前有没有出现右括号的数量大于左括号的数量的情况,如果出现这种情况
就在从最左边到当前位置找一个 由某个由问号转变而来的右括号 变成左括号
当然这个转变要增加的花费肯定是最小的,所以也就转换成了求区间最值的问题,线段树可以搞
不过考虑到碰到上述情况时要替换的位置肯定在之前,这时候与后面没关系,因为肯定要把前面的某个右括号变回左括号
所以用个堆或者set保存前面存下来的信息就可以了
学了点c++的东东:try catch()
View Code
#include<cstdio> #include<set> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef __int64 lld; const int M = 50010; int a[M]; char str[M]; int main(){ lld ans=0; int i,j,b,m,n; scanf("%s",str); n=strlen(str); m=count(str,str+n,'?'); for(i=0;i<m;i++){ scanf("%d%d",&a[i],&b); a[i]-=b; ans+=b; } try { set<pair<int,int> > st; m=0;b=0; for(i=0;i<n;i++){ if(str[i]=='(') ++b; else if(str[i]==')') --b; else { str[i]=')'; --b; st.insert(make_pair(-a[m++],i)); } if(b<0){ if(st.empty()) throw 0; ans-=(--st.end())->first; str[(--st.end())->second]='('; st.erase(*(--st.end())); b+=2; } } if(b!=0) throw 1; printf("%I64d\n%s\n",ans,str); }catch(...){ puts("-1"); } return 0; }