zoukankan      html  css  js  c++  java
  • NOIP 2016

      • Prob.1 玩具谜题

    模拟、、

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    struct node{
    	int dir;
    	char name[15];
    }nd[100005];
    int p,n,m; 
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=0;i<n;i++) scanf("%d %s",&nd[i].dir,nd[i].name);
    	for(int i=1,a,b;i<=m;i++){
    		scanf("%d%d",&a,&b);
    		b%=n;
    		if(a^nd[p].dir) p+=b;
    		else p-=b; 
    		p=(p+n)%n;
    	}
    	printf("%s",nd[p].name);
    	return 0;
    }
    
      • Prob.2 天天爱跑步

    图1

    代码:

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #define MAXN 300005
    using namespace std;
    struct player{
    	int s,t,l;
    }p[MAXN];
    struct edge{
    	int to,next;
    }E[MAXN*2],sE[MAXN],tE[MAXN],lE[MAXN];
    int Head[MAXN],sHead[MAXN],tHead[MAXN],lHead[MAXN];
    int fa[MAXN],dep[MAXN],tim[MAXN],c1[MAXN],c2[MAXN*2],ans[MAXN];
    int Ent=2,sEnt=2,tEnt=2,lEnt=2;;
    int n,m;
    int find(int u){
    	if(fa[u]==u) return u;
    	return fa[u]=find(fa[u]);
    }
    void add(int u,int v,int &ent,int *head,edge *e){
    	e[ent]=(edge){v,head[u]};
    	head[u]=ent++;
    }
    void Tarjan(int u,int f){
    	fa[u]=u; dep[u]=dep[f]+1;
    	for(int i=sHead[u];i;i=sE[i].next){
    		int j=sE[i].to;
    		if(!fa[p[j].t]||p[j].l) continue;
    		int lca=find(p[j].t);
    		p[j].l=dep[u]+dep[p[j].t]-2*dep[lca];
    		add(lca,j,lEnt,lHead,lE);
    	}
    	for(int i=tHead[u];i;i=tE[i].next){
    		int j=tE[i].to;
    		if(!fa[p[j].s]||p[j].s==p[j].t) continue;
    		int lca=find(p[j].s);
    		p[j].l=dep[u]+dep[p[j].s]-2*dep[lca];
    		add(lca,j,lEnt,lHead,lE);
    	}
    	for(int i=Head[u];i;i=E[i].next){
    		int v=E[i].to;
    		if(v==f) continue;
    		Tarjan(v,u); 
    	}
    	fa[u]=f;
    }
    void dfs(int u,int f){
    	int bc1=c1[dep[u]+tim[u]];
    	int bc2=c2[tim[u]-dep[u]+MAXN];
    	for(int i=sHead[u];i;i=sE[i].next){
    		c1[dep[u]]++;
    	}
    	for(int i=tHead[u];i;i=tE[i].next){
    		int j=tE[i].to;
    		c2[p[j].l-dep[u]+MAXN]++;
    	}
    	for(int i=Head[u];i;i=E[i].next){
    		int v=E[i].to;
    		if(v==f) continue;
    		dfs(v,u); 
    	}
    	ans[u]+=c1[dep[u]+tim[u]]-bc1;
    	ans[u]+=c2[tim[u]-dep[u]+MAXN]-bc2;
    	for(int i=lHead[u];i;i=lE[i].next){
    		int j=lE[i].to;
    		if(dep[p[j].s]-dep[u]==tim[u]) ans[u]--;
    		c1[dep[p[j].s]]--;
    		c2[p[j].l-dep[p[j].t]+MAXN]--;
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,a,b;i<n;i++){
    		scanf("%d%d",&a,&b);
    		add(a,b,Ent,Head,E);
    		add(b,a,Ent,Head,E);
    	}
    	for(int i=1;i<=n;i++) scanf("%d",&tim[i]);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d",&p[i].s,&p[i].t);
    		add(p[i].s,i,sEnt,sHead,sE);
    		add(p[i].t,i,tEnt,tHead,tE);
    	}
    	Tarjan(1,0);
    	dfs(1,0);
    	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    	return 0;
    }
    

    Vijos上好像栈空间不够,要RE4组。把栈空间开大了一些,在本机测试NOI官网上的数据是AC了的。

      • Prob.3 换教室

    dp[i][j][0/1] 当前第i节课,已经申请了j次,当前是否申请的最小疲劳值。
    定义出来以后,就比较好转移了。
       
    之前定义错了,搞了好久。
    启示:本题虽然是计算期望,但求得是最小期望。
        而导致期望有大有小的原因就是我们的申请的位置不同。
        即 申请的位置 这是一个决策选择,用dp处理。
           
        而对于已经 申请了的位置,即确定了申请方案后,因为申请是否成功是有概率的,
        所以再求出其对应的期望。
           
    所以本题就是dp决策出最优申请方案,再在dp的同时求出对应的期望,用以辅助转移。

    代码: 

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define INF 0x3f3f3f3f
    using namespace std;
    double g[2005],dp[2005][2005][2],ans;
    int c[2005],d[2005];
    int dis[305][305];
    int N,M,V,E;
    void cmin(double &a,double b){
    	if(a>b) a=b;
    }
    void readin(){
    	memset(dis,0x3f,sizeof(dis));
    	scanf("%d%d%d%d",&N,&M,&V,&E);
    	for(int i=1;i<=N;i++) scanf("%d",&c[i]);
    	for(int i=1;i<=N;i++) scanf("%d",&d[i]);
    	for(int i=1;i<=N;i++) scanf("%lf",&g[i]);
    	for(int i=1,u,v,w;i<=E;i++){
    		scanf("%d%d%d",&u,&v,&w);
    		dis[u][v]=min(dis[u][v],w);
    		dis[v][u]=min(dis[v][u],w);
    	}
    	for(int i=1;i<=V;i++) dis[0][i]=0,dis[i][i]=0;
    }
    void floyd(){
    	for(int k=1;k<=V;k++)
    		for(int i=1;i<=V;i++)
    			for(int j=1;j<=V;j++){
    				if(dis[i][k]==INF||dis[j][k]==INF) continue;
    				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    			}
    }
    void DP(){
    	for(int i=1;i<=N;i++)
    		for(int j=0;j<=M;j++) dp[i][j][0]=dp[i][j][1]=1e9;
    	dp[1][0][0]=dp[1][1][1]=0;
    	for(int i=2;i<=N;i++)
    		for(int j=0;j<=min(i,M);j++){
    			//不选择申请 
    			cmin(dp[i][j][0],dp[i-1][j][0]
    							 +dis[c[i-1]][c[i]]);//前面不申请 
    							 
    			cmin(dp[i][j][0],dp[i-1][j][1]
    							 +dis[c[i-1]][c[i]]*(1.0-g[i-1])+dis[d[i-1]][c[i]]*g[i-1]);//前面申请 
    			//选择申请
    			if(!j) continue;
    			cmin(dp[i][j][1],dp[i-1][j-1][0]
    							 +dis[c[i-1]][c[i]]*(1.0-g[i])+dis[c[i-1]][d[i]]*g[i]);//前面申请
    							 
    			cmin(dp[i][j][1],dp[i-1][j-1][1]
    							 +dis[c[i-1]][c[i]]*(1.0-g[i-1])*(1.0-g[i])
    							 +dis[c[i-1]][d[i]]*(1.0-g[i-1])*g[i]
    							 +dis[d[i-1]][c[i]]*g[i-1]*(1.0-g[i])
    							 +dis[d[i-1]][d[i]]*g[i-1]*g[i]);
    		}
    	ans=dp[N][0][0];
    	for(int i=1;i<=M;i++) cmin(ans,min(dp[N][i][0],dp[N][i][1]));
    	printf("%.2lf",ans);
    }
    int main(){
    	readin();
    	floyd();
    	DP();
    	return 0;
    }
      • Prob.4 组合数问题

    注意到多组输入都是相同的k
    所以跑一个2000*2000的组合数递推求法,并对k取模
    最后在矩阵合法范围内的0的个数就是答案。
    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    int dp[2005][2005],s[2005][2005];
    int n,m,k,t;
    int main(){
    	scanf("%d%d",&t,&k);
    	dp[1][1]=1;
    	for(int i=2;i<=2001;i++)
    		for(int j=1;j<=i;j++)
    			dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%k; 
    	for(int i=1;i<=2001;i++)
    		for(int j=1;j<=2001;j++)
    			s[i][j]=(j<=i&&dp[i][j]==0)+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    	while(t--){
    		scanf("%d%d",&n,&m);
    		printf("%d
    ",s[n+1][m+1]);
    	}
    	return 0;
    }
      • Prob.5 蚯蚓

    优先队列维护 50分
       
    然后看了看网上给的正解,原来还可以这么单调啊。
    因为割的比例固定,所以:
    长的蚯蚓割了形成的前一段长度大于短的蚯蚓割了形成的前一段,
    长的蚯蚓割了形成的后一段长度大于短的蚯蚓割了形成的后一段。
    维护三个队列(手写)
    第一个用来存储初始蚯蚓,按从大到小排好序。
    第二个队列用来存储割断的蚯蚓的前一截。 (满足长度单调递减)
    第三个队列用来存储割断的蚯蚓的后一截。 (满足长度单调递减)
    对于当前取出的长度为x1的蚯蚓,把它割断成为了c1',c2',并放在对应的队列后面
    x秒后,c1=c1'+x*d(增量) c2=c2'+x*d。
    这时再取出的长度为x2+x*d的蚯蚓,把它割断成为了e1,e2,并放在对应的队列后面
    那是否c1>e1,c2>e2呢?
    考虑c1和e1的大小关系。
    c1=q*x1+x*d
    e1=q*(x2+x*d)
    相减: c1-e1=q(x1-x2)+x*d*(1-q)
    因为x1>x2,1>q,所以上式>0
    所以满足队列具有单调性。
    于是每次取出三个队列队首最大的那个来割。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define INF 0x3f3f3f3f
    using namespace std;
    double p;
    int q[3][10000005],head[3]={1,1,1},tail[3];
    int n,m,d,u,v,t,add;
    bool cmp(int a,int b){
    	return a>b;
    }
    void get(int &mv){
    	mv=-INF; int mp;
    	for(register int i=0;i<3;i++)
    		if(head[i]<=tail[i]&&q[i][head[i]]>mv) 
    			mv=q[i][head[i]],mp=i;
    	head[mp]++;
    }
    void print(int val,int i,int lim){
    	if(i%t) return;
    	printf("%d",val);
    	if(i+t<=lim) printf(" ");
    }
    int main(){
    	freopen("earthworm.in","r",stdin);
    	freopen("earthworm.ans","w",stdout);
    	scanf("%d%d%d%d%d%d",&n,&m,&d,&u,&v,&t);
    	p=1.0*u/v; tail[0]=n;
    	for(register int i=1;i<=n;i++) scanf("%d",&q[0][i]);
    	sort(q[0]+1,q[0]+n+1,cmp);
    	for(register int i=1,mv;i<=m;i++){
    		get(mv); mv+=add;
    		print(mv,i,m);
    		add+=d;
    		int a=(int)(p*mv),b=mv-a;
    		q[1][++tail[1]]=a-add;
    		q[2][++tail[2]]=b-add;
    	}
    	printf("
    ");
    	for(register int i=1,mv;i<=n+m;i++){
    		get(mv); mv+=add;
    		print(mv,i,m+n);
    	}
    	fclose(stdout);
    	return 0;
    }
    
      • Prob.6 愤怒的小鸟

    预处理好转移数组(即打某两个猪的同时,最多可以打那些猪)

    然后就是状压dp。

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define rint register int
    using namespace std;
    const double eps=1e-7;
    struct pig{
    	double x,y;
    }p[20];
    int g[20][20],dp[1<<18]; 
    int n,m,T,all;
    int sign(double x){
    	if(fabs(x)<=eps) return 0;
    	return x>0?1:-1;
    }
    void cmin(int &a,int b){
    	if(a>b) a=b;
    }
    bool get(int i,int j,double &a,double &b){
    	static double k11,k12,k13,k21,k22,k23,k;
    	k11=p[i].x*p[i].x; k12=p[i].x; k13=p[i].y;
    	k21=p[j].x*p[j].x; k22=p[j].x; k23=p[j].y;
    	if(!sign(k12-k22)) return 0;
    	if(!sign(k13/k12-k23/k22)) return 0;
    	k=k22/k12; a=(k23-k13*k)/(k21-k11*k);
    	k=k21/k11; b=(k23-k13*k)/(k22-k12*k);
    	if(sign(a)>0) return 0;
    	return 1;
    }
    bool check(int i,double a,double b){
    	static double x,y;
    	x=p[i].x; y=p[i].y;
    	return !sign(a*x*x+b*x-y);
    }
    int main(){
    	freopen("angrybirds.in","r",stdin);
    	freopen("angrybirds.out","w",stdout);
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d",&n,&m); all=(1<<n)-1;
    		memset(dp,0x3f,sizeof(dp));
    		memset(g,0,sizeof(g));
    		for(rint i=0;i<n;i++)
    			scanf("%lf%lf",&p[i].x,&p[i].y);
    		for(rint i=0;i<n;i++)
    			for(rint j=0;j<n;j++) if(i!=j){
    				double a,b;
    				if(!get(i,j,a,b)) continue;
    				for(rint k=0;k<n;k++) if(check(k,a,b))
    					g[i][j]|=(1<<k);
    			}
    		dp[0]=0;
    		for(rint S=0;S<=all;S++)
    			for(rint i=0;i<n;i++){ 
    				if(S&(1<<i)) continue;
    				cmin(dp[S|(1<<i)],dp[S]+1);
    				for(rint k=1;k<=n;k++) if(i!=k)
    					cmin(dp[S|g[i][k]],dp[S]+1);
    		}
    		printf("%d
    ",dp[all]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    当前信息型强人工智能发展缺失条件--规则
    假象篇(1)-动态可变参数的神经网络
    02梦断代码阅读笔记
    结队开发之NABCD
    01梦断代码阅读笔记
    03构建之法阅读笔记
    进度3
    02构建之法阅读笔记
    01构建之法阅读笔记
    关于最大子序和的算法问题(二)
  • 原文地址:https://www.cnblogs.com/zj75211/p/7774304.html
Copyright © 2011-2022 走看看