【NOIP2015 提高组】

DAY 1

T1 神奇的幻方

简单的模拟~~~~~
#include<bits/stdc++.h>
using namespace std;
#define N 50
int n;
int f[N][N];
bool law1(int x,int y){
	return (x==1 && y != n) ? true : false;}
bool law2(int x,int y){
	return (x!=1 && y == n) ? true : false;}
bool law3(int x,int y){
	return (x==1 && y == n) ? true : false;}
bool law4(int x,int y){
	return (x!=1 && y != n) ? true : false;}
int main()
{
//	freopen("magic.in","r",stdin);
//	freopen("magic.out","w",stdout);
	scanf("%d",&n);
	f[1][n/2+1] = 1;
	int x = 1,y = n/2+1;
	for(int k=2; k<=n*n; k++){
		if(law1(x,y)){
			x = n;
			y++;
		}else
		if(law2(x,y)){
			x--;
			y = 1;
		}else
		if(law3(x,y)){
			x++;
		}else
		if(law4(x,y)){
			if(f[x-1][y+1] == 0){
				x--;
				y++;
			}else x++;
		}
		f[x][y] = k;
	}
	for(int i=1; i<=n; i++){
		for(int j=1; j<n; j++)
			printf("%d ",f[i][j]);
		printf("%d\n",f[i][n]);
	}
}

T2 信息传递

一开始感觉有点像并查集啊,但是不知道具体怎么实现。后来发现可以判断最小环,DFS不解释

*

  • 考场提交代码有两个点TLE,应该是DFS中vis数组相关的问题,很迷~~~
    考场代码:
#include<bits/stdc++.h>
using namespace std;
#define N 200001
#define Max 0x7f7f7f7f
int n,ans=Max;
int t[N];
bool vis[N];
int pos[N];
void Dfs(int x,int sum,int Start){
	if(sum >= ans || sum > n) return;
	if(x == Start){
		ans = min(ans,sum);
		return;
	}
	if(!vis[x]){
		vis[x] = true;pos[x] = sum;
		Dfs(t[x],sum+1,Start);
		vis[x] = false;pos[x] = 0;
	}else{
		ans = min(ans,sum - pos[x]);
	}
	return;
} 
int main()
{
//	freopen("message.in","r",stdin);
//	freopen("message.out","w",stdout);
	scanf("%d",&n);
	for(int i=1; i<=n; i++) scanf("%d",&t[i]);
	for(int i=1; i<=n; i++)
		if(!vis[i]) Dfs(t[i],1,i);
	printf("%d",ans);
	return 0;
}

AC攻略

#include<bits/stdc++.h>
using namespace std;
#define N 200001
#define Max 0x7f7f7f7f
int n,ans=Max;
int t[N];
bool vis[N],all_vis[N];
int pos[N];
void Dfs(int x,int sum,int Start){
	if(sum >= ans || sum > n || all_vis[x]) return;
	if(x == Start){
		ans = min(ans,sum);
		return;
	}
	if(!vis[x]){
		vis[x] = true;pos[x] = sum;
		Dfs(t[x],sum+1,Start);
		all_vis[x] = true;
	}else{
		ans = min(ans,sum - pos[x]);
	}
	return;
} 
int main()
{
//	freopen("message.in","r",stdin);
//	freopen("message.out","w",stdout);
	scanf("%d",&n);
	for(int i=1; i<=n; i++) scanf("%d",&t[i]);
	for(int i=1; i<=n; i++)
		if(!all_vis[i]) Dfs(t[i],1,i);
	printf("%d",ans);
	return 0;
}

T3 斗地主

考场模拟n<=4的情况,结果输入n位置写错,导致零分
正解就是先出顺子,再搞散牌!!!

十七张牌,我不信你能秒我?!
#include<bits/stdc++.h>
using namespace std;
int dp[25][25][25][25],card[15],h[15];
//dp[i][j][k][l]表示i组四张,j组三张,k组两张,l组单牌所需要的最少步数 
int n,t,color,point,ans;
int tnum[5]={0,5,3,2}; 
int dk(int o,int t2,int t3,int f,int k){//处理KING
    if(k==1){
        o++;
        k=0; 
    }
    if(k==0) return dp[f][t3][t2][o];
    else return min(dp[f][t3][t2][o+2],dp[f][t3][t2][o]+1);
}
void dfs(int t){
    if(t>ans) return; 
    memset(h,0,sizeof(h));
    for(int i=2;i<=14;i++) h[card[i]]++;
    ans=min(ans,t+dk(h[1],h[2],h[3],h[4],card[0]));
    for(int k=1;k<=3;k++) //把顺子先处理掉 
     for(int i=3;i<=14;i++)
     {
        int j;
        for(j=i;j<=14&&card[j]>=k;j++)
        {
            card[j]-=k;
            if(j-i+1>=tnum[k]) dfs(t+1);
        }
        for(j--;j>=i;j--) card[j]+=k;
     }
}
void pre()
{
    memset(dp,1,sizeof(dp)); 
    dp[0][0][0][0]=0;

    for(int i=0;i<=n;i++)
     for(int j=0;j<=n;j++)
      for(int k=0;k<=n;k++)
       for(int l=0;l<=n;l++)
        if(i*4+j*3+k*2+l<=n) 
        {
            dp[i][j][k][l]=i+j+k+l; //这是最差的情况 
            if(i){
                if(k>=2) dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k-2][l]+1);// 四带一对  
                if(l>=2) dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k][l-2]+1);// 一对单牌 
                dp[i][j][k][l]=min(dp[i][j][k][l],dp[i-1][j][k][l]+1);           //都不带 
            }
            if(j){
                if(k) dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k-1][l]+1);  // 3带一对 
                if(l) dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k][l-1]+1);  // 3带单 
                dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j-1][k][l]+1);          // 什么都不带 
            }
            if(k) dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k-1][l]+1);        //对子
            if(l) dp[i][j][k][l]=min(dp[i][j][k][l],dp[i][j][k][l-1]+1);        //单牌
        }   
}
int main()
{
    scanf("%d %d",&t,&n);
    pre();
    while(t--)
    {
        memset(card,0,sizeof(card)); 
        ans=n; 
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&point,&color);
            if(point==1) card[14]++; 
            //因为2不能算顺子,所以将A变为14方便发顺子 
            else card[point]++;
        }
        dfs(0); 
        printf("%d\n",ans);
    } 
    return 0;
}

DAY2

T1 跳石头

!!!划重点:最大值最小/最小值最大 => 二分
销魂的码子

#include<bits/stdc++.h>
using namespace std;
#define LL long long
int n,m;
LL d,a[50001];
bool Judge(LL x){
	int last = 0,tot = 0;
	for(int i=1; i<=n; i++){
		if(a[i]-a[last] < x) tot++;
		else last = i;
	}
	return tot <= m;
}
int main()
{
//	freopen("stone.in","r",stdin);
//	freopen("stone.out","w",stdout);
	scanf("%lld%d%d",&d,&n,&m);
	for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
	n++;a[n] = d;
	LL l = 0,r = a[n],mid;
	while(l<=r){
		mid = (l+r)/2;
		if(Judge(mid))
			l = mid+1;
		else
			r = mid-1;
	}
	printf("%lld",r);
	return 0; 
}

T2 子串

看到迪皮就头疼
正解:

  1. 可以看到对于子串个数的判断,只跟前一位的字符有没有取有关
  2. so:f[i][j][k][1/0]表示到A串的第i个位置,匹配到B串的第j个位置,已经用k个字串时,a[i]==b[j]进行或不进行匹配的方案数
  3. and then:滚动数组!
    码子
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define Max 1010
int n,m,k,now;
long long f[2][205][205][2];
char a[Max],b[205];
int main()
{
	scanf("%d%d%d\n%s\n%s",&n,&m,&k,a+1,b+1);
	int now=1,pre=0;
	f[0][0][0][0]=f[1][0][0][0]=1;
	for(int i=1;i<=n;i++){	  
		for(int j=1;j<=m;j++)
	  		for(int g=1;g<=k;g++){
		 		if(a[i]==b[j]) f[now][j][g][1] = (f[pre][j-1][g][1] + f[pre][j-1][g-1][1] + f[pre][j-1][g-1][0]) % mod;
					else f[now][j][g][1] = 0;
	  			f[now][j][g][0] = (f[pre][j][g][0] + f[pre][j][g][1]) % mod;
	   		 }
		swap(now,pre);
	}
	printf("%lld",(f[pre][m][k][0]+f[pre][m][k][1])%mod);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值