zoukankan      html  css  js  c++  java
  • CSP前的做题计划

    2019.10.3:蔡是原罪.所以蒟蒻决定从(1997)年开始刷每年的普及组和提高组的题.普及组应该都会刚完,提高组有些题选择性放弃.

    2019.10.9:很抱歉,我又决定咕咕咕了,因为我发现以前的题太难了!!!!!!!!!!!!!!!!

    2019.10.27:蒟蒻在考完鸽王的模拟赛DAY1之后痛定思痛,决定继续来填这个坑!!!!!!!!

    (1997)普及组/提高组:

    棋盘问题1

    题意:设有一个(N imes M)方格的棋盘((1≤N≤100,1≤M≤100)),求出该棋盘中包含有多少个正方形、多少个长方形(不包括正方形).

    分析:直接推式子.正方形:长和宽相等,直接枚举边长即可,(sum_{i=1}^{min(n,m)}(n-i+1)(m-i+1)).矩形(包括正方形):长和宽可以不等,要分别枚举,(sum_{i=1}^nsum_{j=1}^m(n-i+1)(m-i+1)=sum_{i=1}^n(n-i+1)sum_{j=1}^m(m-i+1)),然后运用等差数列求和公式即可得(frac{n(n+1)}{2}frac{m(m+1)}{2}=(n+1)(m+1)nm/4.)

    长方形数量即为两式相减.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int n,m,ans1,ans2;
    int main(){
    	n=read();m=read();if(n>m)swap(n,m);
    	for(int i=1;i<=n;++i)
    		ans1+=(n-i+1)*(m-i+1);
    	ans2=(n+1)*(m+1)*n*m/4;
    	printf("%d %d
    ",ans1,ans2-ans1);
        return 0;
    }
    
    

    棋盘问题(2)

    题意:在(N imes N)的棋盘上((1≤N≤5)),填入(1,2,…,N^2)(N^2)个数,使得任意两个相邻的数之和为素数.如有多种解,则输出第一行、第一列之和为最小的排列方案;若无解,则输出“NO”.

    直接爆搜的话,因为要保证第一行、第一列之和为最小的排列方案,所以(n=5)会超时,所以就(dfs)的同时记录了第一行第一列上的值,如果已经超过了当前的最小值,直接(return),一个小剪枝即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=15;
    int n,Ans=1e9,a[N][N],BJ[N*N],ans[N][N];
    int prime[N*N],v[N*N],bj[N*N];
    inline void get_prime(){
    	int m=0;
    	for(int i=2;i<=200;++i){
    		if(!v[i]){
    			v[i]=i;BJ[i]=1;
    			prime[++m]=i;
    		}
    		for(int j=1;j<=m;++j){
    			if(prime[j]*i>200||prime[j]>v[i])break;
    			v[prime[j]*i]=prime[j];
    		}
    	}
    }
    inline bool pd(int num,int x,int y){
    	if(bj[num])return 0;
    	if(y>=2&&!BJ[a[x][y-1]+num])return 0;
    	if(x>=2&&!BJ[a[x-1][y]+num])return 0;
    	return 1;
    }
    inline void check(){
    	int cnt=0;
    	for(int i=1;i<=n;++i)cnt+=a[i][1];
    	for(int j=2;j<=n;++j)cnt+=a[1][j];
    	if(cnt>=Ans)return;Ans=cnt;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)
    			ans[i][j]=a[i][j];
    }
    inline void dfs(int x,int y,int now){
    	if(now>=Ans)return;
    	if(y>n){
    		if(x==n){check();return;}
    		else dfs(x+1,1,now);
    	}
    	else{
    		for(int i=2;i<=n*n;++i){
    			if(pd(i,x,y)){
    				bj[i]=1;a[x][y]=i;
    				dfs(x,y+1,x==1||y==1?now+i:now);
    				bj[i]=0;
    			}
    		}
    	}
    }
    int main(){
    	get_prime();n=read();
    	if(n==1){puts("NO");return 0;}
    	a[1][1]=1;bj[1]=1;dfs(1,2,1);
    	if(Ans==1e9)puts("NO");
    	else{
    		for(int i=1;i<=n;++i){
    			for(int j=1;j<=n;++j){
    				printf("%d ",ans[i][j]);
    			}
    			printf("
    ");
    		}
    	}
        return 0;
    }
    
    

    斐波那契数列(升级版)

    题意:请你求出第n个斐波那契数列的数mod(2^{31})之后的值.并把它分解质因数.

    分析:就直接根据题意分两步来做即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    ll mod=1<<31,f[50];
    int p[50],c[50];
    int main(){
    	int n=read();f[1]=1;f[2]=1;
    	if(n==1||n==2){
    		printf("%d=%d
    ",n,n);
    		return 0;
    	}
    	for(int i=3;i<=n;++i)f[i]=(f[i-2]+f[i-1])%mod;
    	ll m=f[n];int sum=0;
    	for(int i=2;i*i<=m;++i){
    		if(m%i==0){
    			p[++sum]=i;
    			while(m%i==0)++c[sum],m/=i;
    		}
    	}
    	if(m>1)p[++sum]=m,c[sum]=1;
    	printf("%lld=",f[n]);
    	for(int i=1;i<=sum;++i){
    		if(i>=2)printf("*");
    		printf("%d",p[i]);
    		for(int j=2;j<=c[i];++j){
    			printf("*%d",p[i]);
    		}
    	}
    	puts("");
        return 0;
    }
    
    

    棋盘问题(2)【加强】

    题意:在(N imes N)的棋盘上((1≤N≤10)),填入(1,2,…,N^2)(N^2)个数,使得任意两个相邻的数之和为素数.

    分析:预处理出(a[i][j])表示加上i之后是质数的第j个数,(b[i][j][k])表示加上i且加上j之后是质数的第k个数,其实就是为了在搜索枚举每个位置填什么数字的时候能够快一点,没必要1到(n^2)的枚举.

    因为题目要保证第一行第一列的和最下,所以先搜索第一行,然后搜索第二列,再搜索剩下的格子.

    然后发现(n=7)时方案不合法,(n=9)超时,打个表算了.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    int n,jz[15][15],bj[105];
    int v[205],prime[105],a[105][105],b[105][105][50];
    inline void prework(){
    	int m=0,max1=n*n,max2=max1*2;
    	for(int i=2;i<=max2;++i){
    		if(!v[i]){
    			v[i]=i;
    			prime[++m]=i;
    		}
    		for(int j=1;j<=m;++j){
    			if(prime[j]>v[i]||prime[j]*i>max2)break;
    			v[i*prime[j]]=prime[j];
    		}
    	}
    	for(int i=1;i<max1;++i)
    		for(int j=i+1;j<=max1;++j)
    			if(v[i+j]==i+j){
    				a[i][++a[i][0]]=j;
    				a[j][++a[j][0]]=i;
    			}
    	for(int i=1;i<max1;++i)
    		for(int j=i+1;j<=max1;++j)
    			for(int k=1;k<=a[i][0];++k)
    				if(v[a[i][k]+j]==a[i][k]+j){
    					b[i][j][++b[i][j][0]]=a[i][k];
    					b[j][i][++b[j][i][0]]=a[i][k];
    				}
    }
    inline void dfs(int now1,int now2){
    	if(now2>n){
    		if(now1==n){
    			for(int i=1;i<=n;++i){
    				for(int j=1;j<=n;++j)
    					printf("%d ",jz[i][j]);
    				printf("
    ");
    			}
    			exit(0);
    		}
    		else dfs(now1+1,2);
    	}
    	int x=jz[now1-1][now2],y=jz[now1][now2-1];
    	for(int i=1;i<=b[x][y][0];++i){
    		if(bj[b[x][y][i]])continue;
    		jz[now1][now2]=b[x][y][i];
    		bj[b[x][y][i]]=1;
    		dfs(now1,now2+1);
    		bj[b[x][y][i]]=0;
    	}
    }
    inline void dfs_lie(int now){
    	if(now>n){dfs(2,2);return;}
    	int last=jz[now-1][1];
    	for(int i=1;i<=a[last][0];++i){
    		if(bj[a[last][i]])continue;
    		jz[now][1]=a[last][i];
    		bj[a[last][i]]=1;
    		dfs_lie(now+1);
    		bj[a[last][i]]=0;
    	}
    }
    inline void dfs_hang(int now){
    	if(now>n){dfs_lie(2);return;}
    	int last=jz[1][now-1];
    	for(int i=1;i<=a[last][0];++i){
    		if(bj[a[last][i]])continue;
    		jz[1][now]=a[last][i];
    		bj[a[last][i]]=1;
    		dfs_hang(now+1);
    		bj[a[last][i]]=0;
    	}
    }
    int main(){
    	scanf("%d",&n);prework();jz[1][1]=1;bj[1]=1;
    	if(n==1){puts("NO");return 0;}
    	if(n==7){
    		printf("1 2 3 4 7 6 5
    ");
    		printf("12 17 14 15 16 25 18
    ");
    		printf("11 20 27 46 37 36 35
    ");
    		printf("8 33 40 43 30 23 38
    ");
    		printf("9 28 19 24 29 44 45
    ");
    		printf("10 31 42 47 32 39 34
    ");
    		printf("13 48 41 26 21 22 49
    ");
    		return 0;
    	}
    	if(n==9){
    		printf("1 2 3 4 7 6 5 8 9
    ");
    		printf("10 21 80 69 40 73 78 71 32
    ");
    		printf("13 76 51 62 27 34 19 60 77
    ");
    		printf("16 25 58 45 26 75 64 67 72
    ");
    		printf("15 28 39 68 35 38 63 46 37
    ");
    		printf("14 33 74 29 54 59 50 81 22
    ");
    		printf("17 20 53 18 49 24 47 56 57
    ");
    		printf("12 41 48 79 30 43 36 23 44
    ");
    		printf("11 42 55 52 31 70 61 66 65
    ");
    		return 0;
    	}
    	dfs_hang(2);puts("NO");
        return 0;
    }
    
    

    (1998)普及组:

    三连击

    题意:将(1,2, cdots ,9)(9)个数分成(3)组,分别组成(3)个三位数,且使这(3)个三位数构成(1:2:3)的比例,试求出所有满足条件的(3)个三位数.

    分析:直接枚举第一个数的每一位,然后表示出第二,三个数,最后(check)一下是否合法即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int bj[10];
    inline bool check(int x,int y,int z){
    	memset(bj,0,sizeof(bj));
    	++bj[x%10];++bj[x/100];++bj[(x/10)%10];
    	++bj[y%10];++bj[y/100];++bj[(y/10)%10];
    	++bj[z%10];++bj[z/100];++bj[(z/10)%10];
    	for(int i=1;i<=9;++i)
    		if(bj[i]!=1)return 0;
    	return 1;
    }
    int main(){
    	for(int i=1;i<=9;++i)
    		for(int j=1;j<=9;++j)
    			for(int k=1;k<=9;++k){
    				int s1=i*100+j*10+k,s2=2*s1,s3=3*s1;
    				if(s3>999)continue;
    				if(check(s1,s2,s3))printf("%d %d %d
    ",s1,s2,s3);
    			}
        return 0;
    }
    
    

    阶乘之和

    题意:用高精度计算出(S=1!+2!+3!+…+n! (n≤50)).其中“!”表示阶乘,例如:(5!=5 imes 4 imes 3 imes 2 imes 1).

    分析:因为不喜欢高精,也不会高精,就直接(kuai)的以前写的.

    #include<bits/stdc++.h>
    using namespace std;
    int len=1,t[10001],ans[10001],anslen,n;
    void jiecheng(int v){ //定义函数计算n的阶乘; 
    	for(int i=1;i<=len;i++){
    		t[i]=t[i]*v; //每个位数都乘v; 
    	}
    	int i=1;
    	while(t[i]>9||i<len){  //判断是否需要进位; 
    		t[i+1]=t[i+1]+t[i]/10; //进位; 
    		t[i]=t[i]%10;   //只保留个位数; 
    		i++;           //继续对下一位进行处理; 
    	}
    	len=i;
    }
    void jia(){
    	for(int i=1;i<=len;i++){
    		ans[i]=ans[i]+t[i]; //对每一位都相加; 
    		if(ans[i]>9){       //当满足进位条件; 
    			ans[i+1]=ans[i+1]+ans[i]/10;  //进位; 
    			ans[i]=ans[i]%10;       //只保留个位; 
    			anslen=max(anslen,i+1);   
    		}
    		anslen=max(anslen,i); //判断当前数的位数,避免了多余的计算; 
    	}
    }
    int main(){
    	scanf("%d",&n);
    	t[1]=1;
    	for(int i=1;i<=n;i++){
    		jiecheng(i),jia();
    	}
    	for(int i=anslen;i>=1;i--){
    		printf("%d",ans[i]);
    	}
    	return 0;
    }
    

    幂次方

    题意略.

    分析:一道分治好题,为什么难度才普及(-),挺难的啊.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int Base[20];
    inline void solve(int x){
    	int m;
    	for(int i=0;i<=15;++i){
    		if(Base[i]>x){
    			m=i-1;
    			break;
    		}
    	}
    	if(m==0)printf("2(0)");
    	if(m==1)printf("2");
    	if(m>=2){
    		printf("2(");
    		solve(m);
    		printf(")");
    	}
    	if(x-Base[m]>0){
    		printf("+");
    		solve(x-Base[m]);
    	}
    }
    int main(){
    	Base[0]=1;for(int i=1;i<=15;++i)Base[i]=Base[i-1]*2;
    	int n=read();solve(n);puts("");
        return 0;
    }
    
    

    1998提高组:

    车站

    题意:火车从始发站(称为第(1)站)开出,在始发站上车的人数为(a),然后到达第(2)站,在第(2)站有人上、下车,但上、下车的人数相同,因此在第(2)站开出时(即在到达第(3)站之前)车上的人数保持为(a)人。从第(3)站起(包括第(3)站)上、下车的人数有一定规律:上车的人数都是前两站上车人数之和,而下车人数等于上一站上车人数,一直到终点站的前一站(第(n-1)站),都满足此规律。现给出的条件是:共有(N)个车站,始发站上车的人数为(a),最后一站下车的人数是(m)(全部下车)。试问(x)站开出时车上的人数是多少?(a(≤20))(n(≤20))(m(≤2000)),和(x(≤20)).

    分析:这道题我推了一个小时的式子,我真的太弱了,而且还是手列表格.

    设第一,二站上车人数为x人:

    [ egin{matrix} num & 1 & 2 & 3 & 4 & 5 & 6 & 7\ up & a & x & a+x & a+2x & 2a+3x & 3a+5x & 5a+8x\ down & 0 & x & x & a+x & a+2x & 2a+3x & 3a+5x\ end{matrix} ]

    然后第n站下车的人数,就是第n-1站开出时车上的人数,第n站是没有人上车的.然后发现这一站下车的人数可以一一和上一站上车的人数抵消,所以第(n-1)站开出时车上的人数为 第一站上车人数+第(n-1)站上车人数-第二站下车人数 .所以有式子(f([(n-1)-2]+1)a+(f[(n-1)-1]-1)x=m)

    解方程解出(x)之后,第p站开出时车上的人数,还是根据上式(ans=f([p-2]+1)a+(f[p-1]-1)x).

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    ll f[20];
    int main(){
    	int a=read(),n=read(),m=read(),x=read();
    	f[1]=1;f[2]=1;for(int i=3;i<=18;++i)f[i]=f[i-2]+f[i-1];
    	int up=(m-(f[n-3]+1)*a)/(f[n-2]-1);
    	printf("%lld
    ",(f[x-2]+1)*a+(f[x-1]-1)*up);
        return 0;
    }
    
    

    拼数

    题意:设有(n)个正整数((n≤20)),将它们联接成一排,组成一个最大的多位整数

    分析:就利用(string)可以直接相加,并且直接比较大小的性质.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    string s[20];
    int main(){
    	int n;cin>>n;
    	for(int i=1;i<=n;++i)cin>>s[i];
    	for(int i=1;i<n;++i)
    		for(int j=i+1;j<=n;++j)
    			if(s[i]+s[j]<s[j]+s[i])swap(s[i],s[j]);
    	for(int i=1;i<=n;++i)cout<<s[i];cout<<endl;
        return 0;
    }
    
    

    进制位

    题意略.

    分析:数论难,字符串细节多(->)这道题好难啊.

    仔细观察一下样例,就会发现,进制=字母个数=(n-1),第二问就这么解决了.然后再仔细观察一下样例,就会发现,对于每一个字母,它那一行上两位数的个数就是它的值(这个值还等于字母总个数-1-该字母在两位数的个位上出现的次数).就这样判断得出答案即可.

    细节问题我写进注释里面.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    string s;char order[10];
    int a[10],bj[10];
    map<string,int>Map;
    map<char,int>b;
    int main(){
    	int n;cin>>n;cin>>s;
    	for(int i=1;i<n;++i){
    		cin>>s;
    		order[i]=s[0];//题目最后要按照出现顺序输出,就记录一下
    	}//把第一行读掉,没什么用
    	for(int i=1;i<n;++i){
    		cin>>s;//把每一行的第一列读掉,没什么用
            Map.clear();//清空map
    		for(int j=1;j<n;++j){
    			cin>>s;
    			if(Map[s]){
    				puts("ERROR!");
    				return 0;
    			}Map[s]=1;
    //同一行上相同字符串只能出现一次,否则无解,亲测这里会有一个点
    			if(s.size()==2){
    				++a[i];
    				++b[s[1]];
    			}
    //对于二位数,a[i]表示第i个字母所对应的行上二位数的个数
    //b数组记录字母在二位数的各位出现的次数
    		}
    	}
    	for(int i=1;i<n;++i)++bj[a[i]];
    	for(int i=0;i<n-1;++i){
    		if(bj[i]!=1){
    			puts("ERROR!");
    			return 0;
    		}
    	}//0到n-2,每个数都要有一个,否则无解
    	for(int i=1;i<n;++i){
    		if(a[i]!=n-2-b[order[i]]){
    			puts("ERROR!");
    			return 0;
    		}
    	}//这个判断亲测会有一个点
    	for(int i=1;i<n;++i)printf("%c=%d ",order[i],a[i]);
    	printf("
    %d
    ",n-1);
        return 0;
    }
    
    

    (1999)普及组:

    Cantor表

    题意:现代数学的著名证明之一是Georg Cantor证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的:

    (1/1) , (1/2) , (1/3) , (1/4), (1/5), …

    (2/1), (2/2) , (2/3), (2/4), …

    (3/1) , (3/2), (3/3), …

    (4/1), (4/2), …

    (5/1), …

    我们以(Z)字形给上表的每一项编号。第一项是(1/1),然后是(1/2)(2/1)(3/1)(2/2),…求表的第(N(N<=10004000))项.

    分析:又是一道普及(-),推了一个小时.主要就是各种分类讨论吧.也不好解释.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int main(){
    	int n=read(),i;
    	for(i=1;i<=5000;++i){
    		if(i*(i+1)/2>n)break;
    	}
    	if(i*(i-1)/2==n){
    		if(i&1)printf("%d/1
    ",i-1);		
    		else printf("1/%d
    ",i-1);
    		return 0;
    	}
    	if(i&1){
    		n-=i*(i-1)/2;
    		printf("%d/%d
    ",i+1-n,n);
    	}
    	else{
    		n-=i*(i-1)/2;
    		printf("%d/%d
    ",n,i+1-n);
    	}
        return 0;
    }
    
    

    回文数

    题意:若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。例如:给定一个十进制数(56),将(56)(65)(即把(56)从右向左读),得到(121)是一个回文数。

    又如:对于十进制数(87)

    STEP1:(87)+(78) = (165)

    STEP2:(165)+(561) = (726)

    STEP3:(726)+(627) = (1353)

    STEP4:(1353)+(3531) = (4884)

    在这里的一步是指进行了一次(N)进制的加法,上例最少用了(4)步得到回文数(4884)

    写一个程序,给定一个(N)((2 le N le 10,N=16))进制数(M)((100)位之内),求最少经过几步可以得到回文数。如果在(30)步以内(包含(30)步)不可能得到回文数,则输出Impossible!.

    分析:又是一道字符串,又搞了一个小时,普及组的题都这么难的么;没有思维难度啊,就每一步按照题意来模拟就行,每得到一个数字就去(check)是否回文即可.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    char s[100005],s1[1000005];
    inline bool check(){
    	int len=strlen(s+1);
    	for(int i=1;i<=len/2;++i){
    		if(s[i]!=s[len+1-i])return 0;
    	}
    	return 1;
    }
    char zm[20]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    int main(){
    	int n,step=0;
    	scanf("%d%s",&n,s+1);
    	while(1){
    		if(check()){
    			printf("STEP=%d
    ",step);
    			return 0;
    		}
    		if(step>=30)break;
    		int len=strlen(s+1),bj=0;
    		for(int i=1;i<=len;++i){
    			int now=s[i]-(s[i]>=65?'A'-10:'0')+s[len+1-i]-(s[len+1-i]>=65?'A'-10:'0')+bj;
    			bj=0;
    			if(now>=n){now-=n;bj=1;}
    			s1[len-i+1]=zm[now];
    		}
    		if(bj){
    			s1[0]='1';
    			for(int i=1;i<=len+1;++i)s[i]=s1[i-1];
    		}
    		else for(int i=1;i<=len;++i)s[i]=s1[i];
    		++step;
    	}
    	puts("Impossible!");
        return 0;
    }
    
    

    导弹拦截

    题意:某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.输入导弹依次飞来的高度(雷达给出的高度数据是$ le 50000$的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统.

    分析:绿题反而更好写???两问毫不相干,分来来做,对于第一问"这套系统最多能拦截多少导弹",设(f[i])表示一套系统打下的第i枚导弹的高度,那么扫描的时候能打就打,不能打就去前面找到第一个比当前导弹低的替换掉.

    第二问也是能打就打,不能打就新建一个系统.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    const int N=100005;
    int a[N],f[N];
    int main(){
    	int high,n=0,now;
    	while(scanf("%d",&high)!=EOF)a[++n]=high;
    	f[1]=a[1];now=1;
    	for(int i=2;i<=n;++i){
    		if(f[now]>=a[i])f[++now]=a[i];
    		else{
    			for(int j=1;j<=now;++j)
    				if(f[j]<a[i]){f[j]=a[i];break;}
    		}
    	}
    	printf("%d
    ",now);
    	f[1]=a[1];now=1;
    	for(int i=2;i<=n;++i){
    		if(f[now]<a[i])f[++now]=a[i];
    		else{
    			for(int j=1;j<=now;++j)
    				if(f[j]>=a[i]){f[j]=a[i];break;}
    		}
    	}
    	printf("%d
    ",now);
        return 0;
    }
    
    

    (1999)提高组:

    旅行家的预算

    题意:一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的).给定两个城市之间的距离(D1)、汽车油箱的容量(C)(以升为单位)、每升汽油能行驶的距离(D2)、出发点每升汽油价格(P)和沿途油站数(N)(N)可以为零),油站(i)离出发点的距离(Di)、每升汽油价格(Pi)(i=1,2,…,N)).计算结果四舍五入至小数点后两位.如果无法到达目的地,则输出("No) (Solution".)

    分析:最不喜欢做贪心+模拟的题了,细节超级多,容许我留个坑.

    邮票面值设计

    题意:给定一个信封,最多只允许粘贴(N)张邮票,计算在给定(K)(N+K≤15))种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值(MAX),使在(1)(MAX)之间的每一个邮资值都能得到.例如,(N=3)(K=2),如果面值分别为(1)分、(4)分,则在(1)分~(6)分之间的每一个邮资值都能得到(当然还有(8)分、(9)分和(12)分);如果面值分别为(1)分、(3)分,则在(1)分~(7)分之间的每一个邮资值都能得到.可以验证当(N=3)(K=2)时,(7)分就是可以得到的连续的邮资最大值,所以(MAX=7),面值分别为(1)分、(3)分.

    分析:直接(dfs)枚举选哪(k)种面值的邮票,每搜索到一个值,就(dp)(这应该算是个多重背包吧)计算当前所能够产生的最大值,以便于搜索数量的减少.

    最近做了好多道先(dfs)穷举状态,然后(dp)计算该状态下的最优解的题.本题还在于(dp)的值有利于我们搜索剪枝.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int n,k,Ans,ans[20],num[20],f[100005];
    inline int dp(int x){
    	for(int i=1;i<=num[x]*n;++i)f[i]=1e9;f[0]=0;
    	for(int i=1;i<=x;++i)
    		for(int j=num[i];j<=num[x]*n;++j)
    			f[j]=min(f[j],f[j-num[i]]+1);
    	for(int i=1;i<=num[x]*n;++i)if(f[i]>n)return i-1;
    	return num[x]*n;
    }
    inline void dfs(int now,int maxn){
    	if(now>k){
    		if(maxn>Ans){
    			Ans=maxn;
    			for(int i=1;i<=k;++i)ans[i]=num[i];
    		}
    		return;
    	}
    	for(int i=num[now-1]+1;i<=maxn+1;++i){//这个枚举的上下界都懂吧
    		num[now]=i;dfs(now+1,dp(now));
    	}
    }
    int main(){
    	n=read();k=read();dfs(1,0);
    	for(int i=1;i<=k;++i)printf("%d ",ans[i]);
    	printf("
    MAX=%d
    ",Ans);
        return 0;
    }
    

    (2000)普及组:

    题意:税收与补贴问题

    这道题不太想讲,因为看懂题意就搞了很久,然后严重怀疑第四个数据点是有问题的,搞得我额外为这个点写了几十行代码,很不爽.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    int a[10000],b[10000],c[10000],d[10000];
    int main(){
    	int n=read(),m=0,pos=0,ans=1e9;
    	while(1){
    		int x=read(),y=read();if(x==-1&&y==-1)break;
    		a[++m]=x;b[m]=y;
    	}
    	int k=read();
    	while(1){
    		if(b[m]<=k)break;
    		a[m+1]=a[m]+1;b[m+1]=b[m]-k;++m;
    	}
    	for(int i=1;i<=m;++i)if(a[i]==n)pos=i;
    	if(!pos){//处理第四个点
    		int xl=abs(b[m]-b[1])/abs(a[m]-a[1]);
    		int tot=1;c[1]=a[1];d[1]=b[1];
    		while(1){
    			if(d[tot]<=xl)break;
    			c[tot+1]=c[tot]+1;d[tot+1]=d[tot]-xl;++tot;
    		}
    		for(int i=1;i<=tot;++i)if(c[i]==n)pos=i;
    		for(int i=-1000;i<=1000;++i){
    			int ppx=(c[pos]-c[1]+i)*d[pos],bj=1;
    			for(int j=1;j<=tot;++j){
    				if(j==pos)continue;
    				if((c[j]-c[1]+i)*d[j]>ppx){
    					bj=0;break;
    				}
    			}
    			if(bj){
    				if(abs(i)<abs(ans))ans=i;
    			}
    		}
    		if(ans==1e9)puts("NO SOLUTION");
    		else printf("%d
    ",ans);
    		return 0;
    	}
    	for(int i=-1000;i<=1000;++i){
    		int ppx=(a[pos]-a[1]+i)*b[pos],bj=1;
    		for(int j=1;j<=m;++j){
    			if(j==pos)continue;
    			if((a[j]-a[1]+i)*b[j]>ppx){
    				bj=0;break;
    			}
    		}
    		if(bj){
    			if(abs(i)<abs(ans))ans=i;
    		}
    	}
    	if(ans==1e9)puts("NO SOLUTION");
    	else printf("%d
    ",ans);
        return 0;
    }
    
    

    计算器的改良

    字符串+大模拟=不想做.

    (2000)提高组

    方格取数

    题意:(n*n)的棋盘,有些点有权值,两次从左上角走到右下角,一个点的权值经过后就变成零了,求最大得分.

    直接设(f[i][j][k][l])暴力转移即可.

    费用流也可以做.时间复杂度都差不多,但是代码量就........

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=10;
    int val[N][N],f[N][N][N][N];
    int main(){
    	int n=read();
    	while(1){
    		int x=read(),y=read(),z=read();
    		if(!x&&!y&&!z)break;val[x][y]=z;
    	}
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)
    			for(int k=1;k<=n;++k)
    				for(int l=1;l<=n;++l){
    					f[i][j][k][l]=max(f[i-1][j][k-1][l],max(f[i-1][j][k][l-1],max(f[i][j-1][k-1][l],f[i][j-1][k][l-1])))+val[i][j]+val[k][l];
    					if(i==k&&j==l)f[i][j][k][l]-=val[i][j];
    				}
    	printf("%d
    ",f[n][n][n][n]);
        return 0;
    }
    
    
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=100005;
    int n,s,t,max_flow,ans,val[10][10];
    int dis[N],visit[N],incf[N],pre[N];
    int tot,head[N],nxt[N],to[N],limit[N],w[N];
    inline void add(int a,int b,int c,int d){
    	nxt[++tot]=head[a];head[a]=tot;to[tot]=b;limit[tot]=c;w[tot]=d;
    	nxt[++tot]=head[b];head[b]=tot;to[tot]=a;limit[tot]=0;w[tot]=-d;
    }
    inline bool spfa(){
    	for(int i=1;i<=2*n*n;++i)dis[i]=-1e9,visit[i]=0;
    	queue<int>q;q.push(s);dis[s]=0;visit[s]=1;incf[s]=1e9;
    	while(q.size()){
    		int u=q.front();q.pop();visit[u]=0;
    		for(int i=head[u];i;i=nxt[i]){
    			if(!limit[i])continue;
    			int v=to[i];
    			if(dis[v]<dis[u]+w[i]){
    				dis[v]=dis[u]+w[i];
    				incf[v]=min(incf[u],limit[i]);
    				pre[v]=i;
    				if(!visit[v])visit[v]=1,q.push(v);
    			}
    		}
    	}
    	if(dis[t]==-1e9)return 0;
    	return 1;
    }
    inline void update(){
    	int x=t;
    	while(x!=s){
    		int i=pre[x];
    		limit[i]-=incf[t];
    		limit[i^1]+=incf[t];
    		x=to[i^1];
    	}
    	max_flow+=incf[t];
    	ans+=dis[t]*incf[t];
    }
    int main(){
    	n=read();tot=1;s=1;t=2*n*n;
    	while(1){
    		int x=read(),y=read(),z=read();
    		if(!x&&!y&&!z)break;val[x][y]=z;
    	}
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n;++j){
    			int num=(i-1)*n+j;
    			add(num,num+n*n,1,val[i][j]);
    			add(num,num+n*n,1,0);
    			if(j+1<=n)add(num+n*n,num+1,2,0);
    			if(i+1<=n)add(num+n*n,num+n,2,0);
    		}
    	}
    	while(spfa())update();
    	printf("%d
    ",ans);
        return 0;
    }
    

    乘积最大

    题意略.

    (DP)很好想,设(f[i][j])表示前i个数分成j段的最大乘积,则(f[i][j]=max(f[k][j-1]+sum[k+1][j])).初始化(f[i][0]=1,f[i][1]=sum[1][i]),目标(f[n][m+1]).

    (long) (long)(60)分,(int128)(80)分,高精(100)分,懒得写了.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    const int N=50;
    int n,m;
    __int128 sum[N][N],f[N][N];
    char s[N];
    inline void print(__int128 x){
    	if(!x)return;
    	if(x<0)putchar('-'),x=-x;
    	print(x/10);putchar(x%10+'0');
    }
    int main(){
    	scanf("%d%d%s",&n,&m,s+1);
    	for(int i=1;i<=n;++i)
    		for(int j=i;j<=n;++j)
    			for(int k=i;k<=j;++k)
    				sum[i][j]=(__int128)sum[i][j]*10+s[k]-'0';
    	for(int i=1;i<=n;++i)f[i][0]=1,f[i][1]=sum[1][i];
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m+1;++j){
    			if(i<j)continue;
    			for(int k=1;k<i;++k){
    				if(k>=j-1)f[i][j]=max(f[i][j],f[k][j-1]*sum[k+1][i]);
    			}
    		}
    	print(f[n][m+1]);puts("");
        return 0;
    }
    
    
  • 相关阅读:
    文学、哲学段子
    文学、哲学段子
    js技术要点---JS 获取网页源代码
    泛型类,泛型方法,泛型委托的定义方法
    数组元素的逆序数
    stm32 ARM中的RO、RW和ZI DATA
    poj 3040 Allowance 贪心
    schedule()函数的调用时机(周期性调度)
    以JTextPanel为例Swing的鼠标事件详解
    实习生面试总结
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11618994.html
Copyright © 2011-2022 走看看