金山西山居初赛第一场

http://acm.hdu.edu.cn/showproblem.php?pid=4545

hdu 4545 魔法串

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 237    Accepted Submission(s): 96


Problem Description
  小明和他的好朋友小西在玩一个新的游戏,由小西给出一个由小写字母构成的字符串,小明给出另一个比小西更长的字符串,也由小写字母组成,如果能通过魔法转换使小明的串和小西的变成同一个,那么他们两个人都会很开心。这里魔法指的是小明的串可以任意删掉某个字符,或者把某些字符对照字符变化表变化。如:
    小西的串是 abba;
    小明的串是 addba;
    字符变化表 d b (表示d能转换成b)。
  那么小明可以通过删掉第一个d,然后将第二个d转换成b将串变成abba。

  现在请你帮忙判断:他们能不能通过魔法转换使两个人的串变成一样呢?
 

Input
  首先输入T,表示总共有T组测试数据(T <= 40)。
  接下来共T组数据,每组数据第一行输入小西的字符串,第二行输入小明的字符串(数据保证字符串长度不超过1000,小明的串的长度大于等于小西的,且所有字符均为小写字母)。接着输入字母表,先输入m,表示有m个字符变换方式(m< = 100),接着m行每行输入两个小写字母,表示前一个可以变为后一个(但并不代表后一个能变成前一个)。
 

Output
  对于每组数据,先输出Case数。
  如果可以通过魔法转换使两个人的串变成一样,输出“happy”,
  否则输出“unhappy”。
  每组数据占一行,具体输出格式参见样例。
 

Sample Input
  
2 abba addba 1 d b a dd 0
 

Sample Output
  
Case #1: happy Case #2: unhappy

#include<cstdio>
#include<cstring>
const int N=10002;
char s1[N],s2[N];
bool g[26][26];
int l1,l2;
int main(){
    int i,j,T,m,ca=1;
    char u[3],v[3];
    scanf("%d",&T);
    while(T--){
        scanf("%s%s",s1,s2);
        l1=strlen(s1),l2=strlen(s2);
        scanf("%d",&m);
		memset(g,0,sizeof(g));
        while(m--){
            scanf("%s%s",u,v);
            g[u[0]-'a'][v[0]-'a']=1;
        }
        for(i=0,j=0;i<l1&&j<l2;j++){
            int p=s1[i]-'a',q=s2[j]-'a';
            if(s2[j]==s1[i]||g[q][p])i++;
        }
        printf("Case #%d: ",ca++);
        puts(i==l1?"happy":"unhappy");
    }
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=4546

hdu 4546比赛难度

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 199    Accepted Submission(s): 35


Problem Description
  最近,小明出了一些ACM编程题,决定在HDOJ举行一场公开赛。
  假设题目的数量一共是n道,这些题目的难度被评级为一个不超过1000的非负整数,并且一场比赛至少需要一个题,而这场比赛的难度,就是所有题目的难度之和,同时,我们认为一场比赛与本场题目的顺序无关,而且题目也不会重复。
  显而易见,很容易得到如下信息:
  假设比赛只用1个题目,有n种方案;
  假设比赛使用2个题目,有(n-1)*n/2种方案;
  假设比赛使用3个题目,有(n-2)*(n-1)*n/6种方案;
  ............
  假设比赛使用全部的n个题目,此时方案只有1种。
  
  经过简单估算,小明发现总方案数几乎是一个天文数字!
  为了简化问题,现在小明只想知道在所有的方案里面第m小的方案,它的比赛难度是多少呢?
 

Input
输入数据的第一行为一个整数T(1 <= T <= 20),表示有T组测试数据。
每组测试数据第一行为两个整数n, m(0 < n, m <= 10000),表示现在有n个题目,现在要求第m小的方案的比赛难度。接下来第二行有n个数字,分别表示这n个题目的难度值。
 

Output
对于每组测试数据,输出一行"Case #c: ans"(不包含引号),ans 表示要求的第m小的比赛难度,输入数据保证存在第m小的方案,具体参见样例。
 

Sample Input
  
2 5 6 1 1 1 1 1 5 25 1 2 3 4 5
 

Sample Output
  
Case #1: 2 Case #2: 11

 

分析:multiset直接搞,保存当前前K个数

#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
multiset<int> S,tmp;
multiset<int>::iterator it,it1;
const int N=10002;
int a[N];
int main(){
	int T,i,n,k,ca=1,t1,t2;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&k);
		S.clear();
		for(i=0;i<n;i++)scanf("%d",a+i);
		sort(a,a+n);
		bool f=0;
		S.insert(0);
		for(i=0;i<n;i++){
			if(f){
				it1=S.end(),it1--;tmp.clear();
                for(it=S.begin();it!=S.end();it++,it1--){
                    t1=(*it),t2=(*it1);
                    if(a[i]+t1<t2)tmp.insert(a[i]+t1),S.erase(S.find(t2));
                    else break;
                }
                for(it=tmp.begin();it!=tmp.end();it++)S.insert((*it));
			}else{
				tmp.clear();
				for(it=S.begin();it!=S.end();it++)tmp.insert(a[i]+(*it));
				for(it=tmp.begin();it!=tmp.end();it++)S.insert((*it));
				if(S.size()>=k+1)f=1;
				while(S.size()>k+1){
					it=S.end();it--;
					S.erase(it);
				}
			}
		}
		it=S.end(),it--;
		printf("Case #%d: %d\n",ca++,(*it));
	}
	return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=4547

hdu 4547 CD操作

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 166    Accepted Submission(s): 48


Problem Description
  在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录。
  这里我们简化一下问题,假设只有一个根目录,CD操作也只有两种方式:
  
  1. CD 当前目录名\...\目标目录名 (中间可以包含若干目录,保证目标目录通过绝对路径可达)
  2. CD .. (返回当前目录的上级目录)
  
  现在给出当前目录和一个目标目录,请问最少需要几次CD操作才能将当前目录变成目标目录?
 

Input
输入数据第一行包含一个整数T(T<=20),表示样例个数;
每个样例首先一行是两个整数N和M(1<=N,M<=100000),表示有N个目录和M个询问;
接下来N-1行每行两个目录名A B(目录名是只含有数字或字母,长度小于40的字符串),表示A的父目录是B。
最后M行每行两个目录名A B,表示询问将当前目录从A变成B最少要多少次CD操作。
数据保证合法,一定存在一个根目录,每个目录都能从根目录访问到。
 

Output
请输出每次询问的结果,每个查询的输出占一行。
 

Sample Input
  
2 3 1 B A C A B C 3 2 B A C B A C C A
 

Sample Output
  
2 1 2


分析:LCA模版题

#include<cstdio>
#include<cstring>
#include<map>
#include<string>
#include<vector>
using namespace std;
const int N=100002;
vector<int> g[N];
map<string,int> m1;
int lv[N],root;
int p[N][15],S[15];
bool vis[N],in[N];
int max(int a,int b){return a>b?a:b;}
int dfs(int u,int fa,int deep){
	int res=deep,i,v;
	p[u][0]=fa;
	lv[u]=deep;
	vis[u]=1;
	for(i=0;i<g[u].size();i++){
		v=g[u][i];
		if(!vis[v])res=max(res,dfs(v,u,deep+1));
	}
	return res;
}
void init(int n){
	int i,j,level;
	for(S[0]=i=1;i<15;i++)S[i]=S[i-1]<<1;
	memset(vis,0,sizeof(vis));
	memset(p,-1,sizeof(p));
	level=dfs(root,-1,1);
	for(i=1;S[i]<=level;i++)for(j=1;j<=n;j++){
		if(p[j][i-1]!=-1)p[j][i]=p[p[j][i-1]][i-1];
	}
}
int LCA(int x,int y){
	if(lv[x]<lv[y])x^=y,y^=x,x^=y;
	int i,log;
	for(log=0;S[log]<=lv[x];log++);
	for(i=--log;i>=0;i--)if(lv[x]-S[i]>=lv[y])x=p[x][i];
	if(x==y)return x;
	for(i=log;i>=0;i--){
		if(p[x][i]!=-1&&p[x][i]!=p[y][i])x=p[x][i],y=p[y][i];
	}
	return p[x][0];
}
int main(){
	int n,m,ca=1,i,cnt,a,b,c,T,ans;
	char st[42],ed[42];
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		cnt=0;m1.clear();
		for(i=0;i<n;i++)g[i].clear();
		for(i=1;i<n;i++){
			scanf("%s%s",st,ed);
			if(!m1[st])m1[st]=++cnt;
			if(!m1[ed])m1[ed]=++cnt;
			a=m1[st]-1,b=m1[ed]-1;
			g[b].push_back(a);
			in[a]=1;
		}
		for(i=0;i<n;i++)if(!in[i]){root=i;break;}
		init(n);
		while(m--){
			scanf("%s%s",st,ed);
			a=m1[st]-1,b=m1[ed]-1;
			c=LCA(a,b);
			ans=lv[a]-lv[c];
			if(c!=b)ans++;
			printf("%d\n",ans);
		}
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值