NOIP2017总结

本文分享了算法竞赛中的实用技巧,包括如何快速定位问题、利用数据特性简化计算,以及常见算法如深度优先搜索、广度优先搜索的应用。作者通过具体题目解析,如小凯的疑惑、逛公园、奶酪等,展示了不同算法的使用场景和注意事项,强调了代码优化、时间复杂度分析的重要性。
摘要由CSDN通过智能技术生成

怎么说呢,算是找到了短板吧。
虽然炸得有点惨,不过接下来一段时间怕是没心情再摸鱼了QAQ

DAY1

  1. 小凯的疑惑

在这里插入图片描述

分析

都说是一道结论题,如果在考场上直接证不出来,可以先编几组数据找找规律,然后反证,用反证法就简单多了。

值得注意的一点就是要开long long

代码
#include<bits/stdc++.h>
 
using namespace std;
 
int main()
{
    long long a,b;
    scanf("%lld%lld",&a,&b);
    printf("%lld\n",a*b-a-b);
    return 0;
}
  1. 时间复杂度

在这里插入图片描述
在这里插入图片描述

分析

一道大模拟,没太多好说的,只是要注意特殊情况,尤其是嵌套

在考的时候,满心以为自己又A了一道题,结果崩到只有20分——大小写,是No而不是NO,我该感谢上苍我还有20分……
当时下来后,我发现了这个事实后瞬间心态爆炸

知我者谓我心忧,不知我者谓我何求,悠悠苍天,此何人哉! QAQ

这道题其实数据有点水 ,比如这样的代码,本来是错的,但也过了(只给了核心代码):

int t,lcl[30];

struct ff
{
    int o,var;
};
void work()
{
    while(t--)
    {
        mem(lcl);
        int lenth,f=1,np,mp=0,p1=0;
        char c=' ',pc='E';
        stack<ff>s;
        read(lenth);
        if(lenth&1) f=2;
        read(np);
            if(np==1) np=0;
            else read(np);
        for(int i=1;i<=lenth;++i){
            c=getchar();
            while(c!='E'&&c!='F') c=getchar();
            if(c=='F') {
                if(pc=='E') p1=0;
                c=getchar();
                c=getchar();
                int y=c-'a',u,v; ff x;
                if(lcl[y]) f=2;
                lcl[y]=1; x.var=y;
                read(u);read(v);
                if(u>v) x.o=-1;
                else if(u==300) x.o=0;
                     else if(v==300) x.o=1;
                          else x.o=0;
                s.push(x);  pc='F';
            }
            else
            {
                if(s.empty()) f=2;
                else
                {
                    ff x=s.top();
                    s.pop();
                    if(x.o==-1)
                    {
                        if(pc=='E') p1=0; 
                    }
                    else
                        if(pc=='E') p1+=x.o;
                        else p1=Max(p1,x.o);
                    if(s.empty())
                    {
                        mp=Max(mp,p1);
                        p1=0;
                    }
                    pc='E';  lcl[x.var]=0;
                }
            }
        }
        
        if(!s.empty()) f=2;
        if(mp!=np&&f!=2) f=0; 
        
        if(f==1) printf("Yes\n");
        else if(f==2) printf("ERR\n");
             else printf("No\n");
    }
}

这代码错就错在没有存下内里并列嵌套循环的最大时间复杂度,而是直接用了最后嵌套的循环当作最大,它是过不了这组数据:

1
12 O(n^4)
F a 1 n
F b 1 n
F c 1 n
F d 1 n
E
E
E
F e 1 n
F f 1 n
E
E
E

正确应输出Yes,这个错误程序输出的是No,不过洛谷与这次考试的数据中应该都没有这样的数据……

下面放正确代码:
(当然有大佬比我写的好看多了也快多了)

代码
/*
User:Mandy.H.Y
Problem:D1T2
Language:c++
*/
 
#include<bits/stdc++.h>
 
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)>(y)?(x):(y)
#define mem(A) memset((A),0,sizeof(A))
 
using namespace std;
 
const int maxn=0;
 
int t;
int lcl[30];				//小写字母的标记 
struct ff{   				//o-这个语句的时间复杂度	var-这个语句的变量
    int o,var,prc,oz;		//prc-这条语句上一个符号('E'或'F')
};							//oz-这个循环里面嵌的循环的最大复杂度 

template<typename T>inline void read(T &x)//读入优化 
{
    x=0;char c=getchar();bool f=0;
    while((c<'0'||c>'9')&&c!='n') {f|=(c=='-');c=getchar();}//如果读到n了,需要特判 
    if(c=='n') {c=getchar(); x=300; return;}				//若读到n,则返回一个大于100的数,方便判断 
    
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48); c=getchar();}
    if(f)x=-x;
}

template<typename T>void putch(const T x)//输出优化 
{
    if(x>9) putch(x/10);
    putchar((x%10)|48);
}

template<typename T>void put(const T x)
{
    if(x<0) putchar('-'),putch(-x);
    else putch(x);
}

void docu()
{
    freopen("1.txt","r",stdin);
}
void readdata()
{
    read(t);
}

void work()
{
    while(t--)
    {
        mem(lcl);
        
        int lenth,p1=0,mp=0,np=0,f=1;//lenth-这个程序的长度		p1-当前循环(无嵌套)的时间复杂度
        char c,pc='E';				//mp-这个程序的最大复杂度	np-给出的时间复杂度
		//pc-上一行开始的字母		//f-用于判断,f==0:No   f==1:Yes  f==2:ERR 
        stack<ff>s;//栈用于存循环语句 
         
        read(lenth);
        if(lenth&1) f=2;//若长度是奇数,则F与E一定不匹配 
         
        read(np);//读入给出的时间复杂度
        if(np==1) np=0;
        else read(np);
        
        for(int i=1;i<=lenth;++i)
        {
            c=getchar();
            while(c!='E'&&c!='F') c=getchar();//用while读掉回车、空格 
            if(c=='F')
            {
                if(pc=='E')
                {
                	if(!s.empty())//维护oz值 
                	{
                		ff z=s.top();
                    	s.pop();
                    	z.oz=Max(z.oz,p1);
                    	s.push(z);
                    
					}
                	p1=0;
                }
                c=getchar();
                c=getchar();
                 
                int y=c-'a',u,v;
                ff x;
                 
                if(lcl[y]) f=2;//标记变量 
                lcl[y]=1; x.var=y;
                 
                read(u);read(v);
                 
                if(u>v) x.o=-1;//计算这条语句的时间复杂度 
                else if(u==v||v<300) x.o=0;
                     else x.o=1;
                
				x.prc=pc;//后续操作 
                x.oz=0;
                s.push(x);
                pc='F';
            }
            else
            {
                if(s.empty()) f=2;
                else
                {
                    ff x=s.top(); s.pop();
                    lcl[x.var]=0;//清除标记 
                    
                    p1=Max(p1,x.oz);//维护p1 
					if(x.o==-1) p1=0;
                    else if(pc=='F') p1=x.o;
						 else p1+=x.o;
					
                    if(s.empty())//维护mp 
                    {
                        mp=Max(mp,p1);
                        p1=0;
                    }
                    else if(x.prc=='E')//维护oz,主要是嵌套中的最后一个 
                    {
                    	ff z=s.top(); s.pop();
                    	z.oz=Max(z.oz,p1);
                    	s.push(z);
                    	p1=0;
					}
					 
                    pc='E';//后续操作 
                     
                }
            }
        }
         
        if(!s.empty()) f=2;//有F无E 
        if(f!=2&&mp!=np) f=0;//保证语法正确后,判断时间复杂度是否计算正确 
         
        if(f==1) printf("Yes\n");//注意大小写!!!!!!! 
        else if(f==2) printf("ERR\n");
             else printf("No\n");
         
    }
}
 
int main()
{
//  docu();
    readdata();
    work();
    return  0;
}
  1. 逛公园

原谅我个蒟蒻+懒人还没调出来……

DAY 2

  1. 奶酪

在这里插入图片描述

分析

方法一:深搜+染色(似乎比并查集还快那么一丢丢)

方法二:并查集

还有一点,记得开 long long 不然要爆,特别是算距离的时候
最后,注意精度,不用开根号,直接平方就行,保证精度

代码
  1. 深搜+染色
/*
User:Mandy.H.Y
Language:c++
Problem:cheese
*/
 
#include<bits/stdc++.h>
 
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define mem(A) memset((A),0,sizeof(A))
 
using namespace std;
 
const int maxn=1005;
 
struct zuobiao
{
    int x,y,z;
}che[maxn];
 
typedef long long ll;
 
int t,n,h,r;
bool vis[maxn];
 
template<typename T>inline void read(T &x)
{
    x=0;char c=getchar();bool f=0;
    while(c<'0'||c>'9') {f|=(c=='-');c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    if(f)x=-x;
}
 
template<typename T>void putch(const T x)
{
    if(x>9) putch(x/10);
    putchar((x%10)|48);
}
 
template<typename T>inline void put(const T x)
{
    if(x<0) putchar('-'),putch(-x);
    else putch(x);
}
 
void docu()
{
    freopen("cheese.txt","r",stdin);
}
 
void readdata()
{
    read(t);
}
 
bool dfs(int u)
{
    if(che[u].z+r>=h) return true;
    vis[u]=1;
     
    ll x1=(ll)che[u].x,yy=(ll)che[u].y,z1=(ll)che[u].z;
    long long r2=(ll)r+r;
    for(int i=1;i<=n;++i)
    {
         
        ll x2=(ll)che[i].x,y2=(ll)che[i].y,z2=(ll)che[i].z;
         
        
        if(che[i].z>che[u].z+r2) break;
        if(i==u||vis[i]) continue;
        if(che[i].z<che[u].z-r2) continue;
         
        ll dis=(x1-x2)*(x1-x2)+(yy-y2)*(yy-y2)+(z1-z2)*(z1-z2);
        if(dis<=r2*r2)
        {
            if(dfs(i)) return true;
        }
    }
     
    return 0;
}
 
bool cmp(zuobiao a,zuobiao b)
{
    return a.z<b.z;
}
 
void work()
{
    while(t--)
    {
        bool judge=0;
        mem(vis);
        read(n);read(h);read(r);
        for(int i=1;i<=n;++i)
        {
            read(che[i].x);
            read(che[i].y);
            read(che[i].z);
        }
        sort(che+1,che+n+1,cmp);//排个序,方便剪枝
         
        int i=1;
        while(che[i].z>=-r&&che[i].z<=r)
        {
            if(i>n) break;
            if(vis[i])
            {
                ++i;
                continue;
            }
            if(dfs(i))
            {
                judge=1;
                break;
            }
            ++i;
        }
         
        if(judge) printf("Yes\n");
        else printf("No\n");
    }
}
 
int main()
{
//  docu();
    readdata();
    work();
    return 0;
}
  1. 并查集
#include<bits/stdc++.h>
using namespace std;

int t,h,f[1005],n,r,p[1005],num=0,w=0;
void init()
{
    freopen("2.txt","r",stdin);
}

void readdata()
{
    scanf("%d",&t);
}

int find(int x)
{
    return f[x]==x?x:f[x]=find(f[x]);
}

void meger(int x,int y)
{
    f[find(y)]=find(x);
}

void work()
{
    for(int g=1;g<=t;g++)
    {
        memset(f,0,sizeof(f));
        memset(p,0,sizeof(p));
        num=0;
        
        long long x[1002],y[1002],z[1002];
        scanf("%d%d%d",&n,&h,&r);
        for(int i=1;i<=n;i++)
        {
            f[i]=i;
            scanf("%lld%lld%lld",&x[i],&y[i],&z[i]);
            if(z[i]<=r) f[i]=0;
            if(z[i]+r>=h)
            {
                num++;
                p[num]=i;
            }
            for(int j=1;j<i;j++)//这里不需要判断,因为必须一个一个地判距离,一个一个地枚举,不能一棒打死一个集合 
                {
                    long long d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]);
                    if(4*(long long)r*r>=d)//注意long long,一般遇到这种,直接全声明成long long, 
                    {
                        if(find(i)==0) meger(f[i],f[j]); 
                        else meger(f[j],f[i]);
                    }
                }
        }
        w=0;
        for(int i=1;i<=num;i++)
        {
            if(find(p[i])==0)
            {
                printf("Yes\n");
                w=1;
                break;
            }
            
        }
        if(w==0)
        printf("No\n");//输出时注意!! 
    }
}

int main()
{
    //init();
    readdata();
    work();
    return 0;
}
  1. 宝藏

在这里插入图片描述

分析

作为一个深搜渣渣,我光荣的爆零了 (其实是我没有交程序)

我说过,我可是搜索要腾飞的人,搜索与我的决斗就在这个寒假

当然不是搜索被我征服就是我把搜索征服

这道题有很多方法,目前我打了一种,其他方法持续更新……

代码
/*
User:Mandy.H.Y
Language:c++
Problem:treasure
*/
 
#include<bits/stdc++.h>
 
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define mem(A) memset((A),0,sizeof(A))
#define mem1(A) memset((A),0x3f3f3f3f,sizeof(A))//注意0x3f3f3f3f 

using namespace std;
 
const int maxn=13;
const int maxm=1005;

int n,m,size=0,ans;
int first[maxn],dep[maxn],fee[8200],edge[maxn][maxn];
 
template<typename T>inline void read(T &x)
{
    x=0;char c=getchar();bool f=0;
    while(c<'0'||c>'9') {f|=(c=='-');c=getchar();}
    while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    if(f)x=-x;
}
 
template<typename T>void putch(const T x)
{
    if(x>9) putch(x/10);
    putchar((x%10)|48);
}
 
template<typename T>inline void put(const T x)
{
    if(x<0) putchar('-'),putch(-x);
    else putch(x);
}
 
void docu()
{
    freopen("treasure.txt","r",stdin);
}

void readdata()
{
    read(n);
    read(m);
    mem1(edge);
	for(int i=1;i<=m;++i)
	{
		int x,y,z;
		read(x);read(y);read(z);
		edge[x][y]=Min(edge[x][y],z);
		edge[y][x]=edge[x][y];
	}
}

void dfs(int S)
{
	for(int i=1;i<=n;++i)
	{//i是走过的点 
		if((1<<(i-1))&S)
		{
			for(int j=1;j<=n;++j)
			{
				int S1=1<<(j-1);
				if(!(S&S1)&&edge[i][j]!=0x3f3f3f3f)
				{//j未走过
				//i可以走到j 
					int fee1=fee[S]+dep[i]*edge[i][j];
					if(fee[S|S1]>fee1)
					{//当前状态的总费用可更新 
						fee[S|S1]=fee1;

						dep[j]=dep[i]+1;
						
						dfs(S|S1);
						
						dep[j]=0x3f3f3f3f;
					}
				}
			}
		}
	}
}

void work()
{
	ans=0x3f3f3f3f;
	mem1(dep);
    for(int i=1;i<=n;++i)
    {
    	mem1(fee);
    	
    	int S=1<<(i-1);
    	
    	dep[i]=1;
    	fee[S]=0;
    	
    	dfs(S);
    	
    	dep[i]=0x3f3f3f3f;
    	
    	ans=Min(ans,fee[(1<<n)-1]);//!!!!!!!
	}
	put(ans);
}
 
int main()
{
//  docu();
    readdata();
    work();
    return 0;
}
  1. 列队

懒人持续更新中……

未完待续……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值