ACM学习笔记--第一次训练赛题解

第一次训练赛题解

1.气球涂色问题

问题如下:
HDU1556
具体题意:
给定N个气球,然后给出N个区间,每次对区间上的气球刷一次色,最后输出每个气球涂了多少次色。
这个问题设计到的知识点是
差分,区间更新,线段树
在此回顾一下差分的相关概念:
差分:
即相邻两个数的差,用一个数组P存储数组A的差分,那么P是A的差分数组,即
P i = A i − A i − 1 P_i=A_i-A_{i-1} Pi=AiAi1
此处要理解为当前项和前一项的差(因为复原改变后的数组时,需要考虑到第一项没有前一项的问题)
P[i]里存放着A[i]和A[i-1]的差,因为A[0]没有前一项,所以P[0]基本用不到。
用于对区间进行处理时有奇效(
现以本题为例,本题是区间加法的一个问题。
实现将数组A[l]到A[r]都加上K。
一般朴素做法(也就是我的做法
如下:

for(int i=l;i<=k;i++)
{
    a[i]=a[i]+k;
}

显然,当区间长度很长,操作次数很多的时候,这种做法时间复杂度会很高(很多段都重复操作了),会TLE(亲测会超时) 。
于是采用差分的方法。
现以对长度为10的数组A进行对区间[l,r]上的数加k的操作为例。
第一步:初始化差分数组

int P[10],A[10]= {0,1,2,3,90,5,6,7,8,9};        
for(int i=1; i<10; i++)                       
{
    P[i]=A[i]-A[i-1];
}

第二步:
令差分数组的第l项加k,第r+1项减k

P[l]+=k,P[r+1]-=k;

为什么这么写呢。
因为区间内的每个数字都增加相同的数,这时区间内的数的差值是不改变的,即差分数组的P[l+1]到P[r]不会改变。
但是相应的P[l]=A[l]-A[l-1]会增加K
P[r+1]=A[r+1]-A[r]会减少K

最后,如果需要得到操作后的数组,只需要运用:

A[i]=A[i-1]+P[i]
即可得到修改后的数组。
for(int i=1;i<=9;i++)
{
    A[i]=P[i]+A[i-1];
}

所以本题的题解:

#include <iostream>
#include <string.h>
using namespace std;

int main()
{
    int N,A,B;
    cin>>N;
    int Sign[100000]= {0};
    int Data[100000]= {0};
    while(N)
    {
        for(int i=0; i<N; i++)
        {
            cin>>A>>B;
            Sign[A]++;
            Sign[B+1]--;
        }
        for(int i=1; i<=N; i++)         这个地方从第一项开始,刚好题目要求也是从第
        {                               一项开始,因为第0项没有前一项,所以复原的时候
            Data[i]=Data[i-1]+Sign[i];  会很麻烦。
            cout<<Data[i];              所以一般情况下会牺牲一个内存单元来换取对第0if(i!=N)					进行操作时的简便性。
                cout <<" "
        }
        cout<<endl;
        memset(Data,0,sizeof(Data));
        memset(Sign,0,sizeof(Sign));
        cin>>N;
    }
    return 0;
}

通过了解,在差分的基础上,这道题进一步可以有线段树的解法,日后再补充。其实是我现在还没学会

2.下象棋(广度优先搜索)

问题如下:
POJ1915
问题描述:
类似中国象棋里的马走“日”字,给出一个棋盘的大小,然后给出一个起始位置和一个终止位置,输出从起始位置走到终止位置的最小步数。
就是一个广度优先搜索问题,直接暴搜也可以过,但是训练当天脑子真的是太混了,前半段还在写着BFS,但是写着写着就成了DFS。。。考完之后复盘代码,哭笑不得,果然还是自己代码量太少了。
题解:

#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
class Node
{
public:
    int x,y;
    int Times;
    Node(int nx,int ny,int Tmptimes)
    {
        x=nx;
        y=ny;
        Times=Tmptimes;
    }
};
int Sign[300][300]={0};
bool judge(int x,int y,int N)
{
    if((x>=0)&&(x<N)&&(y>=0)&&(y<N)&&(Sign[x][y]!=1))
        return true;
    else
        return false;
}
void BFS(int sx,int sy,int ex,int ey,int N)      // 搜索起始位置和终止位置,棋盘的大小
{
    queue<Node>A;
    memset(Sign,0,sizeof(Sign));
    int nx,ny;
    A.push(Node(sx,sy,0));
    while(!A.empty())
    {
        Node iter=A.front();
        A.pop();
        nx=iter.x,ny=iter.y;
        if((nx==ex)&&(ny==ey))
        {
            cout<<iter.Times<<endl;
            break;
        }
        if(judge(nx-1,ny-2,N))                      //后面这几条搜索用两个数组存储然后循环
            {                                       //操作更好,这样写八条确实有点蠢了
                A.push(Node(nx-1,ny-2,iter.Times+1));
                Sign[nx-1][ny-2]=1;
            }
        if(judge(nx-1,ny+2,N))
        {
            A.push(Node(nx-1,ny+2,iter.Times+1));
            Sign[nx-1][ny+2]=1;
        }
        if(judge(nx-2,ny-1,N))
        {
            A.push(Node(nx-2,ny-1,iter.Times+1));
            Sign[nx-2][ny-1]=1;
        }
        if(judge(nx-2,ny+1,N))
            {
                A.push(Node(nx-2,ny+1,iter.Times+1));
                Sign[nx-2][ny+1]=1;
            }
        if(judge(nx+1,ny-2,N))
            {
                A.push(Node(nx+1,ny-2,iter.Times+1));
                Sign[nx+1][ny-2]=1;
            }
        if(judge(nx+2,ny-1,N))
            {
                A.push(Node(nx+2,ny-1,iter.Times+1));
                Sign[nx+2][ny-1]=1;
            }
        if(judge(nx+2,ny+1,N))
            {
                A.push(Node(nx+2,ny+1,iter.Times+1));
                Sign[nx+2][ny+1]=1;
            }
        if(judge(nx+1,ny+2,N))
        {
            A.push(Node(nx+1,ny+2,iter.Times+1));
            Sign[nx+1][ny+2]=1;
        }
    }
}
int main()
{
    int N,Size,sx,sy,ex,ey;
    cin>>N;
    while(N)
    {
        cin>>Size>>sx>>sy>>ex>>ey;
        BFS(sx,sy,ex,ey,Size);
        N--;
    }
    return 0;
}

3.字符串操作(STL中Map和Sort的应用)

题目:
HDU1113
因为我本人STL掌握的并不是很好,这个题参考了其他大佬的代码才A掉,在这里就不写知识点了,放到之后的STL学习笔记中吧。其实就是懒。
题解:

#include<iostream>
#include<algorithm>
#include<map>
#include<string>
using namespace std;
int main()
{
	string index,word;
	map<string,string> dict;
	while(cin>>word&&word!="XXXXXX")
	{
		index=word;
		sort(index.begin(),index.end());
		dict[word]=index;
	}
	while(cin>>word&&word!="XXXXXX")
	{
		bool flag=false;
		sort(word.begin(),word.end());
		for(map<string, string>::iterator it = dict.begin(); it != dict.end(); it++)
		{
			if(it->second==word)
			{
				cout<<it->first<<endl;
				flag=true;
			}
		}
		if(!flag)
            cout<<"NOT A VALID WORD"<<endl;
        cout<<"******"<<endl;
	}
	return 0;
}

4.道路维修问题(并查集)

题目:
HDU1232
题目大意:
给出M个城镇和N条道路,判断还需要修几条道路才能使所有城镇相连(间接相连也可以,例如1和2相连,2和3相连,则认为1和3间接相连)
题解:
没有用树形的结构存储,用了数组来存储。
只需要用并查集处理数据,最后算出集合数,然后减1输出即可。
但是,因为每次都要修改一个结点之前所有结点的根,所以时间复杂度会很高。相应的,还有树形存储结构和优化算法,在此不做详解。其实就是不会。
题解:

#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int Data[1000]={0};
void Judge(int ,int);
int main()
{
    int N,M;
    cin>>N;  // N是城镇数,M是修了的路数
    while(N)
    {
        cin>>M;
        Judge(N,M);
        cin>>N;
    }
    return 0;
}
int Find(int x)
{
    int i=x;
    int j=x;
    while(i!=Data[i])   //找到当前的这个数组的根
    {
        i=Data[i];
    }
    while(j!=i)        // 将叶到根之间所有的点的根都改成同一个根
    {                
        int Tmp=j;
        j=Data[j];
        Data[Tmp]=i;
    }
    return i;
}
void Judge(int N,int M)     //
{
    memset(Data,0,sizeof(Data));
    for(int i=1;i<=N;i++)
    {
        Data[i]=i;
    }
    int A,B;   // AB是路的两个端点
    for(int j=0;j<M;j++)
    {
        cin>>A>>B;
        int LocA=Find(A);
        int LocB=Find(B);
        if(LocA<LocB)
        {
            Data[LocB]=LocA;
        }
        else
        {
            Data[LocA]=LocB;
        }
    }
    int Sum=0;
    for(int i=1;i<=N;i++)
    {
        if(Data[i]==i)
            Sum++;
    }
    cout<<Sum-1<<endl;
}

5. 我还妹看,在此给出链接留作以后填坑

题目:
POJ3714

6.我还妹看,在此给出链接留作以后填坑

题目:
Codeforce170C

总之,这次训练赛,打得很烂,让我充分地认识到自己眼高手低的事实,同时以前学过的知识没有做题加以巩固,所以不能熟练运用。代码量太少了以至于写起来代码很生疏,甚至不会写,以后会加以改正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值