• [noip][2017]


    Day1T1

    30分思路:

    倒着枚举答案z,用扩展欧几里得求解,如果能找到两个非负整数x,y使得ax+by=z则继续枚举,直到无解为止

    100分:

    最适用与考场上的做法,根据30分思路打表找规律。

    30分代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    #define int long long
    int exgcd(int a,int b,int &x,int &y)
    {
        if(!b)
        {
            x=1;y=0;
            return a;
        }
        int t=exgcd(b,a%b,x,y);
        int tmp=x;
        x=y;
        y=tmp-a/b*y;
        return t;
    }
    inline int cei(int x,int y)
    {
        return x%y==0?x/y:x/y+1;
    }
    main()
    {
        int n,m,x,y;
        cin>>n>>m;
        exgcd(n,m,x,y);
        for(int i=10000000;i>1;--i)
        {
            int xx=x*i,yy=y*i;
            if(xx>=0&&yy>=0)
                continue;
            if(xx<0&&yy>=0)
            {
                int z=cei(-xx,m);
                if(yy-z*n<0)
                {
                    printf("%lld",i);
                    return 0;
                }
                else continue;
            }
            if(yy<0&&xx>=0)
            {
                int z=cei(-yy,n);
                if(xx-z*m<0)
                {
                    printf("%lld",i);
                    return 0;
                }
                else continue;
            }
        }
        return 0;
    }
    

    100分代码:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    typedef long long ll;
    int main()
    {
    	ll n,m;
    	cin>>n>>m;
    	cout<<n*m-n-m;
    	return 0;
    }
    

    Day1T2

    思路

    虽然这个题就是模拟,但是坑点还是比较多的,如果不好好读题目就很容易炸掉。首先,对于初始变量大于终止变量的不能进入循环。如果初始变量和终止变量都是n那么算作常数复杂度。ERR优先于NO。然后慢慢地一边打注释一边模拟就好了。

    代码

    #include<cstdio>
    #include<iostream>
    #include<string>
    #include<map>
    #include<cstring>
    using namespace std;
    int t;
    string s;
    map<char,int>ma;
    int read(string c)
    {
        int x=0;
        int len=c.size();
        for(int i=0;i<len;++i)
        {
            if(!isdigit(c[i])) continue;
            x=x*10+c[i]-'0';
        }
        return x;
    }
    int read1(char x[])
    {
        int b=0;
        int len=strlen(x)-1;
        for (int i = 0; i <=len ; ++i)
            if(isdigit(x[i])) b=b*10+x[i]-'0';
        return b;
    
    }
    char zhan[110];
    char cc[3],c[3];
    int pdn[110];
    int main()
    {
    //	freopen("1.in","r",stdin);
        scanf("%d",&t);
        while(t--)
        {
            int w=0,L,bz=1,njs=0,ansbz=0,nmax=0,pdjs=0,top=0,tcbz=-1;
            ma.clear();
            scanf("%d",&L);
            cin>>s;
            if(s=="O(1)")bz=0;//0表示O(1)复杂度,1表示n^w复杂度
            else w=read(s);
            while(L--)
            {
                cin>>c;
                if(c[0]=='F')
                {
                    cin>>c;
                        zhan[++top]=c[0];//记录变量
                        if(ma[c[0]]==1) //判断是不是重名
                        ansbz=1;//ansbz为1答案为'ERR'
                        ma[c[0]]=1;
                    cin>>c;
                    cin>>cc;
                    if(((cc[0]!='n'&&c[0]!='n'&&read1(cc)<read1(c))||(c[0]=='n'&&cc[0]!='n'))&&(tcbz==-1))
                        tcbz=pdjs;
                    if(cc[0]=='n'&&c[0]!='n')
                    {
                        pdn[++pdjs]=1;//1表示为n,2表示为1
                        if(tcbz==-1)
                        {
                            njs++;
                            nmax=max(nmax,njs);
                            if(bz==0&&ansbz!=1)
                                ansbz=2;//ansbz为2答案为'No'
                        }
                    }
                    else
                        pdn[++pdjs]=2;
                }
                else//循环结束
                {
                        ma[zhan[top]]=0;//销毁变量
                        top--;
                        if(tcbz==-1)
                        {
                            if(pdn[pdjs]==1)//将复杂度减去
                            njs--;
                        }
                    pdjs--;//当前层数--
                    if(pdjs==tcbz) tcbz=-1;
                }
            }
            if(top!=0) ansbz=1;
            if(ansbz==1) 
            {
                printf("ERR
    ");
                continue;	
            }
            if(w!=nmax) ansbz=2;
            if(ansbz==2)
            {
                printf("No
    ");
                continue;
            }
            printf("Yes
    ");
        }
    
        return 0;
    }
    

    Day1T3

    30分思路

    对于所有k=0的情况就是找最短路的条数,只要再找最短路的时候加一个判断就行了

    100分思路

    首先明显是dp,设f[i][k]表示到第i个点,路线长度与最短路差的绝度值不超过k的方案数。那么对于一条边u->v转移方程就是f[v][k]+=f[u][dis[v]+k-w-dis[u]],但是必须要求u先被更新完了才能去更新v,拓扑排序又有环,所以考虑记忆化搜索,f(v,k)表示与上方数组相同的含义,只是这时的v变成了u,u变成了v。

    30分代码

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=100000*5+100,M=200000*5+100;
    typedef pair<int,int> pi;
    typedef long long ll;
    int T,dis[N],mod,in[N],n,m,K;
    struct node
    {
    	int v,nxt,w;
    	node()
    	{
    		v=0,nxt=0,w=0;
    	}
    }e[N];
    int ejs,head[N],vis[N];
    int qq[N];
    void add(int u,int v,int w)
    {
    	e[++ejs].v=v;e[ejs].w=w;e[ejs].nxt=head[u];head[u]=ejs;
    
    }
    int ans[N];
    priority_queue<pi,vector<pi>,greater<pi> > q;
    void dijkstral()
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	while(!q.empty()) q.pop();
    	dis[1]=0;
    	q.push(make_pair(dis[1],1));
    	for(int i=1;i<=n;++i)
    	{
    		while(vis[q.top().second]==1&&!q.empty()) q.pop();
    		int u=q.top().second;
    		vis[u]=1;
    		ans[1]=0;
    		for(int j=head[u];j;j=e[j].nxt)
    		{
    			int v=e[j].v;
    			if(dis[v]>dis[u]+e[j].w)
    			{
    				dis[v]=dis[u]+e[j].w;
    				q.push(make_pair(dis[v],v));
    				ans[v]=1;
    			}
    			else if(dis[v]==dis[u]+e[j].w)
    			{
    				ans[v]+=ans[u];
    			}
    		}
    	}
    }
    ll f[N][55];
    int main()
    {
    	scanf("%d",&T);
    	while(T--)
    	{
    		ejs=0;
    		memset(ans,0,sizeof(ans));
    		memset(head,0,sizeof(head));
    		scanf("%d%d%d%d",&n,&m,&K,&mod);
    		for(int i=1;i<=m;++i)
    		{
    			int x,y,z;
    			scanf("%d%d%d",&x,&y,&z);
    			add(x,y,z);
    		}
    		dijkstral();
    		cout<<ans[n]<<endl;
    	}
    	return 0;
    }
    

    100分代码

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<iostream>
    using namespace std;
    const int N=100100,M=200010;
    int n,m,K,t,ejs,mod,flag;
    int head[N],dis[N],vis[N];
    int f[N][60],bz[N][60];
    queue<int>q;
    struct node{
    	int u,v,w,nxt;
    }e[M];
    void add(int u,int v,int w) {
    	e[++ejs].u=u;e[ejs].v=v;e[ejs].w=w;e[ejs].nxt=head[u];head[u]=ejs;
    }
    void spfa() {
    	while(!q.empty()) q.pop();
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[1]=0;
    	q.push(1);
    	while(!q.empty()) {
    		int u=q.front();
    		q.pop();
    		vis[u]=0;
    		for(int i=head[u];i;i=e[i].nxt) {
    			int v=e[i].v;
    			if(dis[v]>dis[u]+e[i].w) {
    				dis[v]=dis[u]+e[i].w;
    				if(!vis[v]) {
    					q.push(v);
    					vis[v]=1;
    				}
    			}
    		}
    	}
    }
    int dfs(int u,int k) {
    	if(bz[u][k]==1||flag==1) return flag=1;
    	if(bz[u][k]==2) return f[u][k];
    	bz[u][k]=1;
    	for(int i=head[u];i;i=e[i].nxt) {
    		int v=e[i].v;
    		int w=k+dis[u]-dis[v]-e[i].w;
    		if(w>K||w<0) continue;
    		f[u][k]+=dfs(v,w);
    		f[u][k]%=mod;
    	}
    	bz[u][k]=2;
    	return f[u][k];
    }
    int main() {
    	scanf("%d",&t);
    	while(t--) {
    		ejs=0;
    		memset(head,0,sizeof(head));
    		scanf("%d%d%d%d",&n,&m,&K,&mod);
    		for(int i=1,x,y,z;i<=m;++i) {
    			scanf("%d%d%d",&x,&y,&z);
    			add(x,y,z);
    		}
    		spfa();
    		memset(head,0,sizeof(head));
    		for(int i=1;i<=m;++i) {
    			swap(e[i].v,e[i].u);
    			e[i].nxt=head[e[i].u];
    			head[e[i].u]=i;
    		}
    		memset(f,0,sizeof(f));
    		memset(bz,0,sizeof(bz));
    		flag=0;
    		f[1][0]=1;
    		int ans=0;
    		for(int i=0;i<=K;++i) {
    			ans+=dfs(n,i);
    			ans%=mod;
    			if(flag==1){
    				ans=-1;
    				break;
    			}
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    Day2T1

    思路

    (n^2)连边,然后从底下开始bfs看能不能搜到上面。

    代码

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int N=1010;
    double n,h,R;
    int S,T;
    queue<int>q;
    struct poin {
        double x,y,z;
    }p[N];
    inline int ab(double x) {
        return x>=0?x:-x;
    }
    struct node {
        int v,nxt;
    }e[N*N];
    int ejs,head[N],vis[N];
    void add(int u,int v) {
        e[++ejs].v=v;e[ejs].nxt=head[u];head[u]=ejs;
    }
    inline int check(int x,int y) {
        return (p[x].x-p[y].x)*(p[x].x-p[y].x)+(p[x].y-p[y].y)*(p[x].y-p[y].y)+(p[x].z-p[y].z)*(p[x].z-p[y].z)<=R*R*4;
    }
    int bfs() {
        while(!q.empty()) q.pop();
        memset(vis,0,sizeof(vis));
        q.push(S);
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int i=head[u];i;i=e[i].nxt) {
                int v=e[i].v;
                if(!vis[v]) {
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
        return vis[T];
    }
    int main() {
        ios::sync_with_stdio(false);
        int t;
        cin>>t;
        while(t--) {
            ejs=0;
            memset(head,0,sizeof(head));
            cin>>n>>h>>R;
            T=n+1,S=0;
            for(int i=1;i<=n;++i) {
                cin>>p[i].x>>p[i].y>>p[i].z;
                if(ab(p[i].z)<=R) add(S,i);
                if(ab(p[i].z-h)<=R) add(i,T);
            }
            for(int i=1;i<=n;++i)
                for(int j=1;j<=n;++j)
                    if(check(i,j)&&i!=j) add(i,j),add(j,i);
            if(bfs())
                printf("Yes
    ");
            else printf("No
    ");
        }
        return 0;
    }
    

    Day2T2

    40分思路

    对于所有v相等的情况,只要floyd一遍找出最短路。然后枚举起始点找最小花费即可。

    100分思路

    100分可以状态压缩,但是还是看了一骗模拟退火的题解。就是在进行prim的过程中,随机的选次优点进行更新。然后多进行几遍找出最优方案即可。

    40分代码

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    const int N=30;
    using namespace std;
    int f[N][N];
    int main() {
        int n,m,Z;
        scanf("%d%d",&n,&m);
        memset(f,0x3f,sizeof(f));
        for(int i=1;i<=n;++i)
            f[i][i]=0;
        for(int i=1;i<=m;++i) {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            Z=z;
            f[x][y]=f[y][x]=1;
        }
        for(int k=1;k<=n;++k)
            for(int i=1;i<=n;++i)
                for(int j=1;j<=n;++j)
                    if(f[i][j]>f[i][k]+f[k][j])
                        f[i][j]=f[i][k]+f[k][j];
        int anss=0x7fffffff;
        for(int i=1;i<=n;++i) {
            int ans=0;
            for(int j=1;j<=n;++j)
                ans+=f[i][j];
            anss=min(anss,ans);
        }
        printf("%d",anss*Z);
        return 0;
    }
    

    100分代码

    #include<cstdlib>
    #include<ctime>
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=20,INF=10000000;
    int vis[N],a[N][N],dis[N];
    int n,m;
    struct node {
        int u,v;
    };
    bool operator < (const node &x,const node &y) {
        return dis[x.u]*a[x.u][x.v]>dis[y.u]*a[y.u][y.v];
    }
    priority_queue<node> q;
    int prim(int S) {
        int ans=0;
        memset(dis,0,sizeof(dis));
        memset(vis,0,sizeof(vis));
        int js=0;
        node ls[N*N];
        node k;
        dis[S]=1;
        vis[S]=1;
        while(!q.empty()) q.pop();
        for(int i=1;i<=n;++i) {//先找到S可以更新的点
            if(a[S][i]<INF){
                node e;
                e.u=S,e.v=i;
                q.push(e);
            }
        }
        for(int i=1;i<n;++i) {
            k=q.top();q.pop();
            while(!q.empty()&&(vis[k.v]||rand()%n<1)) {//找到要用来更新的边
                if(!vis[k.v]) ls[++js]=k;
                k=q.top();
                q.pop();
            }
            vis[k.v]=1;
            dis[k.v]=dis[k.u]+1;//更新v
            //printf("%d
    ",dis[k.u]);
            ans+=dis[k.u]*a[k.u][k.v];//加入生成树
            while(js)//将预存的边加入队列
                q.push(ls[js--]);
            for(int j=1;j<=n;++j) {
                if(a[k.v][j]<INF&&!vis[j]) {//扩展更多的边
                    node e;
                    e.u=k.v;
                    e.v=j;
                    q.push(e);
                }
            }
        }
        return ans;
    }
    int main() {
        srand(time(0));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                a[i][j]=INF;
        for(int i=1;i<=m;++i) {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            a[x][y]=a[y][x]=min(a[x][y],z);
        }
        int ans=INF;
        for(int i=1;i<=1000;++i) 
            for(int j=1;j<=n;++j)
                ans=min(ans,prim(j));
        printf("%d",ans);
        return 0;
    }
    

    Day2T3

    思路##

    还是只会30分模拟思路。

    30分代码

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=1010;
    int q,a[N][N],n,m;
    inline void change(int x,int y) {
        int tmp=a[x][y];
        for(int i=y;i<m;++i)
            a[x][i]=a[x][i+1];
        for(int i=x;i<n;++i)
            a[i][m]=a[i+1][m];
        a[n][m]=tmp;
    }
    int main() {
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
                a[i][j]=(i-1)*m+j;
        while(q--) {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d
    ",a[x][y]);
            change(x,y);
        }
        return 0;
    }
    

    总结

    noip2017题目比较难,但是暴力只要打满了分数还是可观的。day1的暴力分有30+100+30=160。day2有100+40+30=170。这就有330分了,即便day1t2写挂了只要能拿到30分,就还是能拿到一等奖的(260)。所以打好暴力很重要。。

  • 相关阅读:
    element ui 日期选择器的各种快捷选项
    vue实现带搜索的模拟下拉框组件
    前端面试100问(31-40)
    前端面试100问(22-30)
    古德-图灵估计(Good-Turing Estimate)
    拉普拉斯平滑(Laplace Smoothing)
    分类 与 聚类
    推荐系统
    文本聚类(Text clustering)
    文本分类(Text Categorization)
  • 原文地址:https://www.cnblogs.com/wxyww/p/9555269.html
Copyright © 2020-2023  润新知