2015国庆第一场——四川省赛练习

这场比赛赛后反应坑点很多,比如数据太弱,比如各种坑。。。。然而对自己的练习价值还是很大的

首先上个链接:2015四川省赛4436-4445

百度到的题解:点我看百度题解


先分析每个题,再来分析比赛

A:水题:每个开根号判断就好了

int main(){
	//input;
	while(scanf("%d",&n)!=EOF){
		int flag=1;
		for(int i=1;i<=n;i++){
			scanf("%d",&num);
			if (int(sqrt(num*1.0)*sqrt(num*1.0))!=num) flag=0;
		}
		if (flag) puts("Yes");
		else puts("No");
	}
	return 0;
}

B:题意:定义进位,问n个数,两两相加之后共会有多少个进位总数

脑洞数学题,拿个数举例子的话,68823:

要找尾数3的进位,要找其他数的个位大于等于7的

要找尾数23的进位,要找其他数的个位大于等于77的

要找尾数823的进位,要找其他数的个位大于等于177的

要找尾数8823的进位,要找其他数的个位大于等于1177的

要找尾数68823的进位,要找其他数的个位大于等于31177的


现在的思路已经有了,把每个数的尾数全部截出来,然后跟其他的数的同样长度的尾数进行比较,看看能不能进位。。

但是这样的时间是O(n^2)的

但是,我们用最简单的办法减小复杂度!~二分!


所以,最终的方法为,每个数截取同样长度的数位,各个数位单独从小到大查找自己所需要的“补数”,如823找大于等于177的(但是有个细节,找的时候可能会找到“自己”,待会处理),然后累加即可


上代码:(处理点为  ans  - -  的代码)


vector<ll> G[20];
int x[maxn];

inline void in(int &x){
	char ch;
	x=0;
	ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	int n;
	while(scanf("%d",&n)!=EOF){
		for(int i=1;i<=9;i++) G[i].clear();
		for(int i=1;i<=n;i++){
			in(x[i]);
			ll tp=10;
			for(int k=1;k<=9;k++){
				G[k].push_back(x[i]%tp);
				tp*=10;
			}
		}
		for(int i=1;i<=9;i++) sort(G[i].begin(),G[i].end());
		ll ans=0;
		for(int i=1;i<=n;i++){
			ll tp=10;
			for(int k=1;k<=9;k++){
				ll tt=x[i]%tp;
				tt=tp-tt;
				int u=lower_bound(G[k].begin(),G[k].end(),tt)-G[k].begin();
				ans+=G[k].size()-u;
				if (tt<=x[i]%tp) ans--;
				tp*=10;
			}
		}
		printf("%lld\n",ans/2);
	}
	return 0;
}

C题:字符串,给定A和B串,要求在B串中找到A串第一次出现的位置然后删除,将其未删除的左右两部分合并之后继续操作,问最后还剩下哪些字符

首先介绍一个伟大的算法:KMP算法

这个算法的精髓就是Next数组,因为在暴力操作中一旦匹配失败是退回到初始位置继续,而Next数组的处理之后能够退回到“”最有可能"再次匹配的地方(利用之前所有的匹配信息),弄懂了这一点,这个题就好做

然后,很明显,需要kmp_pre处理A串,然后把B串中的匹配字符串删除,未匹配的存在一个新的字符串中,仍然维护B在A中的Next数组

代码如下(特别简洁):其中pos数组仍然代表失去匹配之后下个位置去哪儿找


void kmp_pre(char x[],int m,int Next[]){
	int i=0,j;
	j=Next[0]=-1;
	while(i<m){
		while(j!=-1&&x[i]!=x[j]) j=Next[j];
		Next[++i]=++j;
	}
}

int Next[N],pos[N];
char stk[N];

int KMP_Count(char x[],int m,char y[],int n){
	int i,j,ip;
	kmp_pre(x,m,Next);
	i=j=ip=0;
	while(ip<n){
		stk[i]=y[ip++];
		while(j!=-1&&stk[i]!=x[j]) j=Next[j];
		i++;j++;
		pos[i]=j;
		if (j>=m){
			i-=m;
			j=pos[i];
		}
	}
	return i;
}

char pat[N],src[N];

int main(){
	while(scanf("%s%s",pat,src)==2){
		int l=KMP_Count(pat,strlen(pat),src,strlen(src));
		stk[l]='\0';
		puts(stk);
	}
	return 0;
}

D题:最小点覆盖

弱用二分图匹配水过,,,然而并不能正确的,但是弱弱在这贴个板子

int uN,vN;
int g[maxn][maxn];
int linker[maxn];
bool used[maxn];

bool dfs(int u){
	for(int v=1;v<=vN;v++)
		if (g[u][v]&&!used[v]){
			used[v]=true;
			if (linker[v]==-1||dfs(linker[v])){
				linker[v]=u;
				return true;
			}
		}
	return false;
}

int hungary(){
	int res=0;
	memset(linker,-1,sizeof(linker));
	for(int u=1;u<=uN;u++){
		memset(used,false,sizeof(used));
		if (dfs(u)) res++;
	}
	return res;
}

int main(){
	//input;
	int n,m,u,v;
	while(scanf("%d%d",&n,&m)!=EOF){
		memset(g,0,sizeof(g));
		while(m--){
			scanf("%d%d",&u,&v);
			g[u][v]=g[v][u]=1;
		}
		uN=vN=n;
		printf("%d\n",hungary()/2);
	}
	return 0;
}

在赛后复制了一份巨巨的代码供大家学习暴力的优美姿势:
vector<int> adjs[MAXN];
int last[510];

bool mat[MAXN][MAXN], vis[MAXN];
int n, m;
int res;

bool mustselect(int u) {
    for(int i = 0; i < u; ++i)
        if(!vis[i] && mat[i][u]) return true;
    return false;
}

void dfs(int u, int c) {
    if(c >= res) return ;
    if(u == n) {
        res = c;
    } else {
        vis[u] = true;
        dfs(u + 1, c + 1);
        vis[u] = false;

        if(!mustselect(u)) {
            int t = 0;
            foreach(it, adjs[u]) if(last[*it] == -1) {
                last[*it] = u;
                t++;
            }
            dfs(u + 1, c + t);
            foreach(it, adjs[u]) if(last[*it] == u)
                last[*it] = -1;
        }
    }
}

int solve() {
    memset(vis, 0, sizeof(vis));
    res = n;
    dfs(0, 0);
    return res;
}

int main() {
    while(scanf("%d%d", &n, &m) != EOF) {
        memset(mat, 0, sizeof(mat));
        memset(last, -1, sizeof(last));
        for(int i = 0; i < 30; ++i)
            adjs[i].clear();

        for(int i = 0, a, b; i < m; ++i) {
            scanf("%d%d", &a, &b);
            a--, b--;
            if(a > b) swap(a, b);
            if(b < 30) mat[a][b] = mat[b][a] = true;
            else adjs[a].push_back(b);
        }

        n = min(n, 30);
        printf("%d\n", solve());
    }
    return 0;
}

E题:完美数学题

首先可知,一个任意的长为x宽为y的矩形,如果可以放入n*m的矩形中,那么方案数为(n-x+1)(m-y+1)

现在开始分析:由于对称性,简化细节,保证n大于等于m(如果n<m的话可以swap)

那么,对x和y的限制有三条,x小于等于n,y小于等于m,2(x+y)小于等于k,可知取min()

由于n和m的范围限制,我们可以枚举一维再做考虑,不妨枚举x

则有x的范围为1到min(n,k/2)

那么y的范围是1到min(k/2-x,m)

循环中不能对y循环,但是,发现!!y是从1开始连续的,把公式拆成(n-x+1)(m+1)-y(n-x+1)

y的变化是知道的!所以只需枚举x即可,y得到最大值就可以算出上述的值


int main(){
	//input;
	while(scanf("%lld%lld%lld",&n,&m,&k)!=EOF){
		ans=0;
		if (n<m) swap(n,m);
		for(w=1;w<=min(k/2-1,n);w++){
			l=min(k/2-w,m);
			r=(l+1)*l/2;
			ans+=l*(n-w+1)*(m+1)-(n-w+1)*r;
		}
		cout<<ans<<endl;
	}
	return 0;
}

F题:树状数组维护最大值:也是下载了一份巨巨的代码

思路:枚举10000这个数据点,然后用树状数组维护终点为i的最大的和(左边求一个,右边求一个),再枚举切断点取最大值即可


int lowbit(int x){
	return x&(-x);
}

void modify(int x,int val){
	while(x){
		tree[x]=max(tree[x],val);
		x-=lowbit(x);
	}
}

int query(int x){
	int res=0;
	while(x<=m){
		res=max(res,tree[x]);
		x+=lowbit(x);
	}
	return res;
}

void init(int n){
	memset(tree,0,sizeof(tree));
	lmax[0]=0;
	for(int i=1;i<=n;i++){
		if (a[i]==0||a[i]==m) lmax[i]=lmax[i-1];
		else{
			lmax[i]=a[i]+query(a[i]);
			modify(a[i],lmax[i]);
			lmax[i]=max(lmax[i],lmax[i-1]);
		}
	}
	memset(tree,0,sizeof(tree));
	rmax[n+1]=0;
	for(int i=n;i>0;i--){
		if (a[i]==0||a[i]==m) rmax[i]=rmax[i+1];
		else{
			rmax[i]=a[i]+query(a[i]);
			modify(a[i],rmax[i]);
			rmax[i]=max(rmax[i],rmax[i+1]);
		}
	}
}

int solve(){
	int res=0;
	for(int i=0;i<n;i++)
		if (b[i]==m){
			for(int j=1;j<n;j++) a[j]=b[(i+j)%n];
			init(n-1);
			res=max(res,max(lmax[1],rmax[n-1]));
			for(int i=1;i<n-1;i++)
				res=max(res,lmax[i]+rmax[i+1]);
		}
	return res+m;
}

int main(){
	//input;
	while(scanf("%d",&n)!=EOF){
		for(int i=0;i<n;i++) scanf("%d",&b[i]);
		printf("%d\n",solve());
	}
	return 0;
}

G和H弱弱不会。。。。。。。。。。


I题:坑广搜(暴力还是保平安啊。。。。。)

题意:给定n点完全图,其中m条边长度为a,剩下的所有边长度都为b,求1到n的最短路径

这个题的最大难度不是思路,而是数据量:n最大100000,所以存数据不能用矩阵,跑算法不能O(n^2)

弱比赛的时候没过,赛后巨巨们的思路是用链表或者并查集之类的数据结构维护:当前那些点没有访问过


代码如下:

int head[MAXV], ecnt;
int to[MAXE], nxt[MAXE];
int n, m, a, b;

void init() {
    memset(head + 1, -1, n * sizeof(int));
    ecnt = 0;
}

void add_edge(int u, int v) {
    to[ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt++;
    to[ecnt] = u; nxt[ecnt] = head[v]; head[v] = ecnt++;
}

int dis[MAXV];

int solvea() {
    fill(dis + 1, dis + n + 1, b);
    dis[1] = 0;

    queue<int> que; que.push(1);
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for(int p = head[u]; ~p; p = nxt[p]) {
            int v = to[p];
            if(dis[u] + a < dis[v]) {
                dis[v] = dis[u] + a;
                que.push(v);
            }
        }
    }
    return dis[n];
}

int cnt[MAXV];

int solveb() {
    fill(dis + 1, dis + n + 1, a);
    dis[1] = 0;

    priority_queue<pair<int, int> > que;
    memset(cnt + 1, 0, n * sizeof(int));
    for(int p = head[1]; ~p; p = nxt[p])
        cnt[to[p]]++;
    for(int i = 2; i <= n; ++i)
        que.push(make_pair(-cnt[i], i));
    int viscnt = 1, d = b;

    while(!que.empty() && d < a) {
        int upper = viscnt;
        while(!que.empty() && -que.top().first < upper) {
            int c = -que.top().first, u = que.top().second; que.pop();
            if(cnt[u] != c) continue;
            viscnt++;
            dis[u] = d;
            for(int p = head[u]; ~p; p = nxt[p]) {
                int v = to[p];
                if(dis[v] == a && cnt[v] < upper) {
                    cnt[v]++;
                    que.push(make_pair(-cnt[v], v));
                }
            }
        }
        d += b;
        if(viscnt == upper) break;
    }
    return dis[n];
}

int main() {
    while(scanf("%d%d%d%d", &n, &m, &a, &b) != EOF) {
        init();
        for(int i = 0, u, v; i < m; ++i) {
            scanf("%d%d", &u, &v);
            add_edge(u, v);
        }
        if(a < b) printf("%d\n", solvea());
        if(a == b) printf("%d\n", a);
        if(a > b) printf("%d\n", solveb());
    }
}
(虽然比赛的时候各种水的姿势都能过。。。然而,赛后学到方法为好)


J题:暴力枚举题

从(0,0)开始,根据题意,暴力找石头看看会不会撞到,如果会,找一个最近的提前转弯

注意细节点:(1)最大值不够大,在比较点的时候会WA(2)4种方向的最大最小不同,而且求最近

struct NODE
{
	int x,y;
	int tow;
	int vis[4];
}node[2000];

int main(){
	//input;
	int i,j,k,l,t,n,ans;
	while(scanf("%d",&n)!=EOF){
	memset(node,0,sizeof(node));
	for(i=1;i<=n;i++)
		scanf("%d%d",&node[i].x,&node[i].y);
	NODE now;
	now.tow=0;
	now.x=now.y=0;
	ans=0;
	while(1){
		t=0;
		int mini=INF,maxi=-INF;
		if(now.tow==0){
			for(i=1;i<=n;i++){
				if(node[i].y==now.y&&node[i].x>now.x&&mini>node[i].x){
					mini=node[i].x; t=i; 
				}
			}
			if(t!=0) now.x=node[t].x-1;
		}
		if(now.tow==1){
			for(i=1;i<=n;i++){
				if(node[i].x==now.x&&node[i].y<now.y&&maxi<node[i].y){
					maxi=node[i].y; t=i; 
				}
			}
			if(t!=0) now.y=node[t].y+1;
		}
		if(now.tow==2){
			for(i=1;i<=n;i++){
				if(node[i].y==now.y&&node[i].x<now.x&&maxi<node[i].x){
					maxi=node[i].x; t=i; 
				}
			}
			if(t!=0) now.x=node[t].x+1;
		}
		if(now.tow==3){
			for(i=1;i<=n;i++){
				if(node[i].x==now.x&&node[i].y>now.y&&mini>node[i].y){
					mini=node[i].y; t=i; 
				}
			}
			if(t!=0) now.y=node[t].y-1;
		}
		if(t==0){
			cout<<ans<<endl; 
			break;	
		}
		else if(node[t].vis[now.tow]==1){
			cout<<-1<<endl; break;
		}
		else{
			ans++;
			node[t].vis[now.tow]=1;
			now.tow=(now.tow+1)%4;
		}
	}
	}
	return 0;
}

谢谢xdlove巨巨和y761823巨巨的赛后分享代码和思路

————————————————————————————————————————————————————————

今天,中国篮球又让我找到了自己的兴奋点!明天加油!

全部评论

相关推荐

10-09 09:39
门头沟学院 C++
HHHHaos:这也太虚了,工资就一半是真的
点赞 评论 收藏
分享
最近又搬回宿舍了,在工位坐不住,写一写秋招起伏不断的心态变化,也算对自己心态的一些思考表演式学习从开始为实习准备的时候就特别焦虑,楼主一开始选择的是cpp后端,但是24届这个方向已经炸了,同时自己又因为本科非92且非科班,所以感到机会更加迷茫。在某天晚上用java写出hello&nbsp;world并失眠一整晚后选择老本行干嵌入式。理想是美好的,现实情况是每天忙但又没有实质性进展,总是在配环境,调工具,顺带还要推科研。而这时候才发现自己一直在表演式学习,徘徊在设想如何展开工作的循环里,导致没有实质性进展。现在看来当时如果把精力专注在动手写而不是两只手端着看教程,基本功或许不会那么差。实习的焦虑5月,楼主...
耶比:哲学上有一个问题,玛丽的房间:玛丽知道眼睛识别色彩的原理知道各种颜色,但是她生活在黑白的房间里,直到有一天玛丽的房门打开了她亲眼看到了颜色,才知道什么是色彩。我现在最大可能的减少对非工作事情的思考,如果有一件事困扰了我, 能解决的我就直接做(去哪里或者和谁吵架等等……),解决不了的我就不想了,每一天都是最年轻的一天,珍惜今天吧
投递比亚迪等公司10个岗位 > 秋招被确诊为…… 牛客创作赏金赛
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务