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;
}

 

 

posted @ 2012-05-21 17:13  Because Of You  Views(275)  Comments(0Edit  收藏  举报