信息竞赛--打表法讲解

对于数据小又容易超时的题,可以采取打表法

打表就是将所有输入情况的答案保存在代码中,输入数据后直接输出就可以了

打表法具有快速,易行(可以写暴力枚举程序)的特点,缺点是代码可能太大,或者情况覆盖不完

对于不会超时,数据规模适合打表,为了简洁你也可以打表

例一:NOIP2008T2

这道题n<=20完全可以打表,代码(生成答案):

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main(){
	freopen("ans.txt","w",stdout);
	int a[10]={6,2,5,5,4,5,6,3,7,6};
	int i,j,temp=0,num=0,k,in[2020],n;
	in[0]=6;
	for(i=1;i<=2000;i++){
		k=i;
		temp=0;
		while(k){
			temp+=a[k%10];
			k/=10;
		}
		in[i]=temp;
	}
	n=0;
	Again:
	num=0;
	for(i=0;i<=999;i++){
		for(j=0;j<=999;j++){
			if(n==in[i]+in[j]+in[i+j]+4) num++;
		}
	}
	printf("%d,",num);
	if(n<24){n++;goto Again;}
	return 0;
}

提交代码:

#include<iostream>
using namespace std;
int ans[]={0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,8,9,6,9,29,39,38,65,88,128};
int n;
int main(){
	cin>>n;
	cout<<ans[n]<<'\n';
	return 0;
}

是不是很简洁

例二:骨牌问题[3]

Description

  在《骨牌问题[版本1]》中问题描述为:“有 2 行 n 列的长方形,用 n 个 1*2 的骨牌铺满,请计算有多少种铺法。”我们知道这个问题可用递推算法来解决,递推方程如下:
   d[1]=1;
   d[2]=2;
   d[n]=d[n-1]+d[n-2] (n>2)
  现在我们把这个问题推广一下:“有 m 行 n 列的长方形,用 (m*n)/2 个 1*2 的骨牌铺满,请计算有多少种铺法。

Input

若干行,每行包含两个整数:m,n,表示长方形的行和列的数量。

Output

若干行,每行一个整数,对应输入中的长方形铺满的方案数。

Sample Input 1 

1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11

Sample Output 1

1
0
1
2
3
5
144
51205

Hint

m*n<=121

由于目前主要目的是讲打表法,此题解法不重点说明,反正正解是用的状压DP

这道题m*n<=121完全可以用打表的方法,先状压DP生成表:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,ALL;
ll d[121][2058];
int A,B;
void shift(int i,int j,int A_){
	if(j>n-1){
		d[i][B]=d[i][B]+d[i-1][A_];
		return;
	}
	if(A_&(1<<j)){
		B=B&(~(1<<j)); shift(i,j+1,A_);
	}
	if(j<n-1&&(A_&(1<<j))&&(A_&(1<<(j+1)))){
		B|=(1<<j);B|=(1<<(j+1));shift(i,j+2,A_);
	}
	if(!(A&(1<<j))){
		B|=(1<<j);shift(i,j+1,A_);
	}
}
void dp(){
	if(n>m) swap(n,m);
	ALL=(1<<n)-1;
	memset(d,0,sizeof(d));
	d[0][ALL]=1;
	for(int i=0;i<m;i++)
	for(A=0;A<=ALL;A++){
		B=0;
		shift(i+1,0,A);
	}
}
int main(){
	freopen("ans.txt","w",stdout);//输出到答案表中 
	int cnt=0;
	printf("\t");
	for(int i=1;i<=121;i++)
	for(int j=1;i*j<=121;j++) if(i*j%2==0&&i<=j){//枚举情况 
		n=i;m=j;
		dp();//状压DP 
		cnt++;
		printf("f[%d][%d]=%lldLL",i,j,d[m][ALL]);
		printf("%s",cnt%3?",":";\n\t");//格式控制 
	}
	return 0;
}

然后把答案copy到程序中就可以了:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll f[20][150];
int x,y;
void get_V(){
	f[1][2]=1LL,f[1][4]=1LL,f[1][6]=1LL;
	f[1][8]=1LL,f[1][10]=1LL,f[1][12]=1LL;
	f[1][14]=1LL,f[1][16]=1LL,f[1][18]=1LL;
	f[1][20]=1LL,f[1][22]=1LL,f[1][24]=1LL;
	f[1][26]=1LL,f[1][28]=1LL,f[1][30]=1LL;
	f[1][32]=1LL,f[1][34]=1LL,f[1][36]=1LL;
	f[1][38]=1LL,f[1][40]=1LL,f[1][42]=1LL;
	f[1][44]=1LL,f[1][46]=1LL,f[1][48]=1LL;
	f[1][50]=1LL,f[1][52]=1LL,f[1][54]=1LL;
	f[1][56]=1LL,f[1][58]=1LL,f[1][60]=1LL;
	f[1][62]=1LL,f[1][64]=1LL,f[1][66]=1LL;
	f[1][68]=1LL,f[1][70]=1LL,f[1][72]=1LL;
	f[1][74]=1LL,f[1][76]=1LL,f[1][78]=1LL;
	f[1][80]=1LL,f[1][82]=1LL,f[1][84]=1LL;
	f[1][86]=1LL,f[1][88]=1LL,f[1][90]=1LL;
	f[1][92]=1LL,f[1][94]=1LL,f[1][96]=1LL;
	f[1][98]=1LL,f[1][100]=1LL,f[1][102]=1LL;
	f[1][104]=1LL,f[1][106]=1LL,f[1][108]=1LL;
	f[1][110]=1LL,f[1][112]=1LL,f[1][114]=1LL;
	f[1][116]=1LL,f[1][118]=1LL,f[1][120]=1LL;
	f[2][2]=2LL,f[2][3]=3LL,f[2][4]=5LL;
	f[2][5]=8LL,f[2][6]=13LL,f[2][7]=21LL;
	f[2][8]=34LL,f[2][9]=55LL,f[2][10]=89LL;
	f[2][11]=144LL,f[2][12]=233LL,f[2][13]=377LL;
	f[2][14]=610LL,f[2][15]=987LL,f[2][16]=1597LL;
	f[2][17]=2584LL,f[2][18]=4181LL,f[2][19]=6765LL;
	f[2][20]=10946LL,f[2][21]=17711LL,f[2][22]=28657LL;
	f[2][23]=46368LL,f[2][24]=75025LL,f[2][25]=121393LL;
	f[2][26]=196418LL,f[2][27]=317811LL,f[2][28]=514229LL;
	f[2][29]=832040LL,f[2][30]=1346269LL,f[2][31]=2178309LL;
	f[2][32]=3524578LL,f[2][33]=5702887LL,f[2][34]=9227465LL;
	f[2][35]=14930352LL,f[2][36]=24157817LL,f[2][37]=39088169LL;
	f[2][38]=63245986LL,f[2][39]=102334155LL,f[2][40]=165580141LL;
	f[2][41]=267914296LL,f[2][42]=433494437LL,f[2][43]=701408733LL;
	f[2][44]=1134903170LL,f[2][45]=1836311903LL,f[2][46]=2971215073LL;
	f[2][47]=4807526976LL,f[2][48]=7778742049LL,f[2][49]=12586269025LL;
	f[2][50]=20365011074LL,f[2][51]=32951280099LL,f[2][52]=53316291173LL;
	f[2][53]=86267571272LL,f[2][54]=139583862445LL,f[2][55]=225851433717LL;
	f[2][56]=365435296162LL,f[2][57]=591286729879LL,f[2][58]=956722026041LL;
	f[2][59]=1548008755920LL,f[2][60]=2504730781961LL,f[3][4]=11LL;
	f[3][6]=41LL,f[3][8]=153LL,f[3][10]=571LL;
	f[3][12]=2131LL,f[3][14]=7953LL,f[3][16]=29681LL;
	f[3][18]=110771LL,f[3][20]=413403LL,f[3][22]=1542841LL;
	f[3][24]=5757961LL,f[3][26]=21489003LL,f[3][28]=80198051LL;
	f[3][30]=299303201LL,f[3][32]=1117014753LL,f[3][34]=4168755811LL;
	f[3][36]=15558008491LL,f[3][38]=58063278153LL,f[3][40]=216695104121LL;
	f[4][4]=36LL,f[4][5]=95LL,f[4][6]=281LL;
	f[4][7]=781LL,f[4][8]=2245LL,f[4][9]=6336LL;
	f[4][10]=18061LL,f[4][11]=51205LL,f[4][12]=145601LL;
	f[4][13]=413351LL,f[4][14]=1174500LL,f[4][15]=3335651LL;
	f[4][16]=9475901LL,f[4][17]=26915305LL,f[4][18]=76455961LL;
	f[4][19]=217172736LL,f[4][20]=616891945LL,f[4][21]=1752296281LL;
	f[4][22]=4977472781LL,f[4][23]=14138673395LL,f[4][24]=40161441636LL;
	f[4][25]=114079985111LL,f[4][26]=324048393905LL,f[4][27]=920471087701LL;
	f[4][28]=2614631600701LL,f[4][29]=7426955448000LL,f[4][30]=21096536145301LL;
	f[5][6]=1183LL,f[5][8]=14824LL,f[5][10]=185921LL;
	f[5][12]=2332097LL,f[5][14]=29253160LL,f[5][16]=366944287LL;
	f[5][18]=4602858719LL,f[5][20]=57737128904LL,f[5][22]=724240365697LL;
	f[5][24]=9084693297025LL,f[6][6]=6728LL,f[6][7]=31529LL;
	f[6][8]=167089LL,f[6][9]=817991LL,f[6][10]=4213133LL;
	f[6][11]=21001799LL,f[6][12]=106912793LL,f[6][13]=536948224LL;
	f[6][14]=2720246633LL,f[6][15]=13704300553LL,f[6][16]=69289288909LL;
	f[6][17]=349519610713LL,f[6][18]=1765722581057LL,f[6][19]=8911652846951LL;
	f[6][20]=45005025662792LL,f[7][8]=1292697LL,f[7][10]=53175517LL;
	f[7][12]=2188978117LL,f[7][14]=90124167441LL,f[7][16]=3710708201969LL;
	f[8][8]=12988816LL,f[8][9]=108435745LL,f[8][10]=1031151241LL;
	f[8][11]=8940739824LL,f[8][12]=82741005829LL,f[8][13]=731164253833LL;
	f[8][14]=6675498237130LL,f[8][15]=59554200469113LL,f[9][10]=14479521761LL;
	f[9][12]=1937528668711LL,f[10][10]=258584046368LL,f[10][11]=3852472573499LL;
	f[10][12]=65743732590821LL;
}
int main(){
	get_V();
	while(scanf("%d%d",&x,&y)==2){
		if(x>y) swap(x,y);
		printf("%lld\n",f[x][y]);
	}
	return 0;
}

值得注意的是,long long赋值的时候建议加上LL后缀

还有,当m*n为奇数是由于全局变量默认为0,正好输出0

例三:

【训练题】错排问题

Description

  班里出了各淘气包,经常搞的老师哭笑不得。淘气包今天将班里每个同学的钥匙放在别人的橱柜里,这样每个人的橱柜里放着的都不是自己的钥匙。当然,也就都锁不上自己的橱柜了。

  老师对淘气包说:“你在考验老师,那么老师也考考你,好不好?”淘气包跃跃欲试,老师的问题是:淘气包这种放置钥匙的方法,会有多少种不同的情况呢?

Input

一个整数n:橱柜的个数即不同钥匙的个数(n<=12)

Output

一个整数:不同的方案数。

Sample Input 1 

3

Sample Output 1

2

Hint

n<=12

这道题其实是一个数学或动态规划(递推)题,面对n<=12的规模暴力排列生成超时,也可以打表

#include<iostream> 
#include<cstdio>
#include<algorithm>
using namespace std;
int a[20],n,ans[20]={0,0,1,2,9,44,265,1854,14833,133496,1334961,14684570,176214841};
/*
bool judge(int w){
	for(int i=1;i<=w;i++){
		if(a[i]==i) return false;
	}
	return true;
}
*/
int main(){
/*
递推方程分析:
1.分析:暴力枚举加打表 
2.现在我会分析了:f(i)=(i-1)(f(i-1)+f(i-2))
方法:设两个递推方程,消去一个就可以了
f(n+1)=n*g(n)    g(n)=(n-1)*g(n-1)+f(n-1) 
*/ 
/*
	for(int n=2;n<=12;n++){
		ans[n]=0;
		for(int i=1;i<=n;i++) a[i]=i;
		while(next_permutation(a+1,a+n+1)){
			if(judge(n)) ans[n]++;
		}
		printf("%d,",ans[n]);
	}
*/
	scanf("%d",&n);
	printf("%d\n",ans[n]);
	return 0;
}

其他应用:

a.生成小范围的素数表,欧拉函数表

b.生成某些需要的常数

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/81188352
今日推荐