您的位置:首页 > 其它

Cf 101 Div.2

2012-02-07 19:53 253 查看
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 ≤ 105, 1 ≤ L ≤ 109).

然后是n组数据: xi, di, ti, pi (0 ≤ xi ≤ L, 1 ≤ di, ti, pi ≤ 109, xi + 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

具体看代码,很值得反复思考的一个题目。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: