Cf 101 Div.2

A题

出现的字符及其次数全部匹配即可。


B题

推规律,注意细节,比如中线什么的。


C题

想法题,好题。

给定n<=3000个人,排队,给定他们每个人前面严格比之高的人的个数num[i],要求你输出他们的高度(高度随意指定,但必须满足他们每个人前面严格比之高的人的个数)。

我的做法:

输入的个数从小到大排序后,第一个必须: num[1]=0, 否则输出 -1;

所有(当然包括第一个人)num[i]的人的高度指定是 5000,

比如num[]= {  -1,0,0,0,2,2,3,4,5...  };

开始是 5000 5000 5000

然后对于num[4]=2, 我在已经构造的序列里找第 (2+1) 个位置,并且取[ 1, 2 ]的最小值val=5000, 将val-1赋给第4个人。

==》 5000 5000 4999 5000

然后 num[5]=2,

==> 5000 5000 4999 4999 5000

然后num[6]=3,

==> 5000 5000 4999 4998 4999 5000

类推。。。我这种做法保证新插入的数比后面的数都更小,所以不会影响后面的人。


D题

看题解的,又看了他人代码。

[0,L]跑道上,有n个跨栏,n and L (0 ≤ n ≤ 1051 ≤ L ≤ 109).

然后是n组数据: xiditipi (0 ≤ xi ≤ L1 ≤ di, ti, pi ≤ 109xi + di ≤ L)。

xi是坐标,该人必须从xi-pi处开始作跨栏的准备,在xi处跳跃,然后飞跃di的距离,飞跃时间是ti,在地上的时间都是1s一个单位距离。使用跨栏必须是 x轴正方向。


算法是:

1、建图:

xi-pi  到 xi+di,权值: pi+ti;

对所有加入的点集合,按坐标从小到大排序,每相邻的点建立边。 同时记得还要建立反边。(必须要有反边,是这题很值得思考的地方,因为可以为了利用某个好的跨栏(飞跃时间短,距离长)而逆向跑(时间短)的情况。)

dijkstra+set即可。

map处理输入数据,建立映射的代码也很不错。

代码:

/*Feb 3, 2012 5:54:16 PM yimao D-Take-off Ramps	 GNU C++ Accepted 530ms 23800KB*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
using namespace std;
#define MM(a,b) memset(a,b,sizeof(a));
typedef unsigned long long u64;
typedef long long lld;
const int maxint= 2000000000;
#define maxn 200100

int n,L,top;
struct Edge{
    int u,v,w,id;
    Edge *next;
}*adj[maxn], edge[maxn*3];
void Add_edge(int u,int v,int w,int id){
    Edge *ptr= &edge[++top];
    ptr->u= u; ptr->v= v; ptr->w= w;
    ptr->id= id;
    ptr->next= adj[u];
    adj[u]= ptr;
}

int cnt;
map<int,int>mm;

int dis[maxn],Index[maxn],pre[maxn];
bool use[maxn];
struct cmp{
    bool operator()(int i,int j)const{
        return dis[i]<dis[j] || (dis[i]==dis[j]&&i<j);
    }
};
set<int,cmp>Q;
void Dijkstra(int st,int N){
    for(int i=1;i<=N;++i) dis[i]= maxint, use[i]=0;
    dis[st]=0;
    if( !Q.empty() ) Q.clear();
    Q.insert( st );
    while( !Q.empty() ){
        int u= *Q.begin();
        Q.erase(u);
        use[u]= 1;
        if( u==2 ) return; // L-->2,improve speed;
        for( Edge *ptr= adj[u]; ptr; ptr= ptr->next ){
            int v= ptr->v;
            if( use[v]==0 && dis[v]>dis[u]+ ptr->w ){
                Q.erase( v );
                dis[v]= dis[u]+ ptr->w;
                Q.insert( v );
                pre[v]= u;
                Index[v]= ptr->id;
            }
        }
    }
}

int node[maxn];
int main()
{
    //freopen("D.txt","r",stdin);
    int i,x,d,t,p;
    while(cin>>n>>L){
        top=0;
        MM( adj, 0 );
        cnt=0;
        if( !mm.empty() ) mm.clear();
        mm[0]= ++cnt, mm[L]= ++cnt;

        for(i=1;i<=n;++i){
            scanf("%d%d%d%d",&x,&d,&t,&p);
            if( x-p<0 ) continue;
            if( mm.find(x-p)==mm.end() )
                mm[x-p]= ++cnt;
            if( mm.find(x+d)==mm.end() )
                mm[x+d]= ++cnt;
            Add_edge( mm[x-p], mm[x+d], p+t, i );
        }
        map<int,int>::iterator it1,it2;
        for(it1= mm.begin();;++it1){
            it2= it1;
            if( (++it2)==mm.end() ) break;
            Add_edge( it1->second, it2->second, it2->first - it1->first, 0 );
            Add_edge( it2->second, it1->second, it2->first - it1->first, 0 ); //
        }
        Dijkstra(1,cnt);
        printf("%d\n", dis[2]);
        int u= 2, c=0;
        while(u!=1){
            if( Index[u]!=0 ) node[++c]= Index[u];
            u= pre[u];
            //printf("%d ",u);
        }
        printf("%d\n",c);
        for(i=c;i>0;--i){
            printf("%d%c",node[i], i==1 ? '\n' : ' ');
        }
    }
}


E题

神题。其实是水题,考察是经典算法理解的是否透彻,在达尔的指导下会了。

要求构造一个生成树,边的属性有s,m两种,要求所用边的属性各半。

思路: prim算法中,每次取的是连接ST和非ST的点集合的具有最小权值的边,而在这里要变换取属性了。比如对于取非集合的点,如果有某个点,具有s的边到ST同时又具有m的边的ST,那么优先考虑取。否则随意取s,m

具体看代码,很值得反复思考的一个题目。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值