2013.7.19 2013NBUT暑期集训成果赛

  • 转载请注明出处忆梦http://blog.csdn.net/fjy4328286/article/details/9392229




    [A] Bachelor

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • 炎热的暑期集训就要结束了,在这短短的20天,大家都很努力,因为很多都是光棍嘛。balabala所以 Marknoon 先森一直耿耿于怀,毕竟他也是单身嘛。有一天,Marknoon 先森看着一串数字,发现了那个跟他同命相连的数字1,所以他就开始无聊起来,想知道从数字1到数字N,一共出现了几个1。例如N=12,则1的个数为5,出现1的数字分别为1,10,11,12。
  • 输入
  • 输入一个数N(1 <= N <= 2147483647)。
  • 输出
  • 输出从1到N中所有数字里出现 1 的个数。
  • 样例输入
  • 3
    13
    123
  • 样例输出
  • 1
    6
    57
  • 提示
  • 来源
  • Hungar
  • 操作

题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1475

详细分析见:http://blog.csdn.net/fjy4328286/article/details/9392283

//此题不难,但需要费点时间推算
#include<stdio.h>
long long fun(long long n)
{
	long long count = 0;
	long long i = 1;
	long long current = 0,after = 0,before = 0;
	while((n / i) != 0)
	{           
		current = (n / i) % 10;
		before = n / (i * 10);
		after = n - (n / i) * i;
		if (current > 1)
			count = count + (before + 1) * i;
		else if (current == 0)
			count = count + before * i;
		else if(current == 1)
			count = count + before * i + after + 1;
		i = i * 10;
	}
	return count;
}

int main ()
{
	long long n;
	while(scanf("%lld", &n) != EOF)
	{
		long long ans = fun(n);
		printf("%lld\n", ans);
	}
	return 0;
}





  • [B] GO Eight Forwards

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • 有一个 2*n的格子里,你可以选择任意一个格子作为起点,你可以朝着相邻的8个格子行走且一个格子只能被走一次.

    问把所有格子都走一遍有多少种方法!



    如图: 该点可以向如图5个方向移动

  • 输入
  • 输入一个 n (1<=n<=1000)
  • 输出
  • 输出一个整数对1000000007求余
  • 样例输入
  • 1
    2
    3
    998
  • 样例输出
  • 2
    24
    96
    510782696
  • 提示
  • 来源
  • 致我失去的数码相机 = =!!
题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1476

题解 :此题是一个规律题,但是这个规律找的有点太牛逼了……
          附上大神的代码:

先用DFS打出前面几个数,然后发现规律
规律是数据分为2部分相乘,对于第i个数,一部分是2的i次幂,令一部分num2[i]=num2[i-1]+num2[i-2]+2*i;
数据从第4个开始满足这个规律吧。

DFS程序
#include<cstdio>
#include<cstring>
int cnt,n;
int vis[3][1100];
int num[8][2]={1,0,	0,1,	-1,0,	0,-1,
	1,1,	1,-1,	-1,1,	-1,-1};
void DFS(int x,int y,int flag)
{
	int k,tmp_x,tmp_y;
	if(flag==2*n)
	{
		cnt++;
		return ;
	}
	for(k=0;k<8;k++)
	{
		tmp_x=num[k][0]+x;	tmp_y=num[k][1]+y;
		if((tmp_x==1 || tmp_x==2) && tmp_y>0 && tmp_y<=n && !vis[tmp_x][tmp_y])
		{
			vis[tmp_x][tmp_y]=1;
			DFS(tmp_x,tmp_y,flag+1);
			vis[tmp_x][tmp_y]=0;
		}
	}
}

int main()
{
	int i,j;

	while(scanf("%d",&n)==1)
	{	
		memset(vis,0,sizeof(vis));
		cnt=0;
		for(i=1;i<=2;i++)
			for(j=1;j<=n;j++)
			{
				vis[i][j]=1;
				DFS(i,j,1);
				vis[i][j]=0;
			}
			printf("%d\n",cnt);
	}

	return 0;
}

源程序
#include<cstdio>

__int64 d[1100];
__int64 num1[1100],num2[1100];

int main()
{
	int i;
	d[1]=2;    d[2]=24;    d[3]=96;
	num1[2]=4;    num2[2]=6;
	num1[3]=8;    num2[3]=12;

	for(i=4;i<=1000;i++)
	{
		num1[i]=num1[i-1]*2%1000000007;
		num2[i]=(num2[i-1]+num2[i-2]+2*i)%1000000007;
		d[i]=num1[i]*num2[i]%1000000007;
	}
	while(scanf("%d",&i)==1)
	{
		printf("%d\n",d[i]);
	}

	return 0;
}





  • [C] AmeriDarts

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • AmeriDarts is a dangerous but full of glamour. Mr.cai wants to have a try.
    The rule is simple:throw some boomerangs in a line from left to right as quickly as you can. You are asked to throw twice, first time throw A red boomerangs, second time throw B blue boomerangs. Your score is to calculate the shortest length of blue and red boomerang. This rule seems strange? so am I.
  • 输入
  • First there is two integer A(no more than 1000) and B(no more than 1000), then followed two lines consist of those A and B numbers.Note that these two array both in ascending order.
  • 输出
  • For each testcase, output the shortest length between red and blue boomerang in a single line.
  • 样例输入
  • 4 4
    1 2 3 4
    5 6 7 8
    5 5
    5 12 14 15 16
    1 2 7 8 9
    
  • 样例输出
  • 1
    2
    
  • 提示
  • 来源
  • Mr.Cai

题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1477

题解:抽象出来,就是A数组中的每一个数与B数组中的每一个数比较,找出相差最小的差值。

#include<stdio.h>
int abss(int x)
{
	if(x > 0)  return x;
	else       return -x;
}
int A[1111],B[1111];
int main ()
{
	int a, b;
	int i, j;
	while(scanf("%d %d", &a, &b) != EOF)
	{
		for(i = 0; i < a; i++)
			scanf("%d", &A[i]);
		for(i = 0; i < b; i++)
			scanf("%d", &B[i]);

		int min = 0x7fffffff;

		int it_a,it_b;
		int temp;

		for(it_a = 0; it_a < a; it_a++)
		{
			temp = abss(A[it_a] - B[0]);

			for(it_b = 1; it_b < b; it_b++)
			{
				if(temp < abss(A[it_a] - B[it_b]))
					break;
				temp = abss(A[it_a] - B[it_b]);
			}
			min = min < temp ? min : temp;
		}

		printf("%d\n",min);

	}
	return 0;
}









  • [D] 泄密的图片

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • 斯诺登的泄密真是让美国捉襟见肘。今天他偷偷给艾丽叶发了一些数据,说是美国秘密拍摄的一些图片。可是都是四个矩阵为一组的写满数字的数据,要怎么看呢?好在人家发话了:为了不被发现是秘密图片,他把这些图片颜色拆分到三张图中(图B、图C和图D)。第一幅图(图A)是计算最终图片的公式,不过他不能直接透露公式是什么,需要艾丽叶酱自行探索。已知的公式是:
    1.第一幅图某个像素点是1的话,那最终图同样位置的像素点就是图B的像素点
    2.第一幅图某个像素点是3的话,那最终图同样位置的像素点就是图B对应位置的像素点加上图C对应位置的像素点
    3.第一幅图某个像素点是4的话,那最终图同样位置的像素点就是图D的像素点
    4.按照上述的推论,从1计算到7即可。若像素点不是1至7之间,那么最终图对应的位置的像素点就是0.
    5.如果某个点的和大于等于9,那这个位置的像素点就是9.
    艾丽叶没几分钟就发现了所有的秘密,你呢?

  • 输入
  • 第一行是总组数。
    第二三行表明了矩阵的列数与行数(不超过200)。
    接下来是公式图的像素点(值范围为1到7)。
    最后是三张图片的像素点(值范围为1到9)。
  • 输出
  • 对于每组数据,输出其最终图片,即计算结果。
  • 样例输入
  • 1
    3
    3
    123
    456
    722
    111
    111
    111
    222
    222
    222
    333
    333
    333
    
  • 样例输出
  • 123
    345
    622
    
  • 提示
  • 来源
  • Mr.Cai

题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1478

题解:第一幅图为计算最终结果的公式,而怎么推算出这个公式呢?
          根据提示: 像素为1 ,则为对应B像素点
                             像素为3,则为B和C的像素点之和
                             像素为4,则为D像素点
        很容易想的是二进制, 1->1    3->11   4 ->100.
         得出最低位代表图B,第二位代表图C,第三位代表图D

#include<stdio.h>
#define N 205
int A[4][N][N];
int ans[N][N];
int funn(int x)
{
	if(x > 9) return 9;
	else return x;
}
void fun(int x, int y)
{
	switch (A[0][x][y])
	{
	case 1:ans[x][y] =	A[1][x][y];break;
	case 2:ans[x][y] =  A[2][x][y];break;
	case 3:ans[x][y] =  funn(A[1][x][y]+A[2][x][y]) ;break;
	case 4:ans[x][y] =  A[3][x][y];break;
	case 5:ans[x][y] =  funn(A[1][x][y]+A[3][x][y]);break;
	case 6:ans[x][y] =  funn(A[2][x][y]+A[3][x][y]);break;
	case 7:ans[x][y] =  funn(A[1][x][y]+A[2][x][y]+A[3][x][y]);break;
	default:ans[x][y] = 0;break;
	}

}
int main ()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		int n, m;
		int i, j, k;
		scanf("%d %d", &m, &n);//m为列数  n为行数 

		for(k = 0; k < 4; k++)
			for(i = 1; i <= n; i++)
				for(j = 1; j <= m; j++)
					scanf("%1d", &A[k][i][j]);

		for(i = 1; i <= n; i++)
			for(j = 1; j <= m; j++)
				fun(i,j);

		for(i = 1; i <= n; i++)
		{
			for(j = 1; j <= m; j++)
				printf("%d",ans[i][j]);
			printf("\n");
		}
	}
	return 0;
}




  • [E] How many

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • There are N numbers, no repeat. All numbers is between 1 and 120, and N is no more than 60. then given a number K(1 <= K <= 100). Your task is to find out some given numbers which sum equals to K, and just tell me how many answers totally,it also means find out hwo many combinations at most.
  • 输入
  • There are T test cases.
    For each case:
    First line there is a number N, means there are N numbers.
    Second line there is a number K, means sum is K.
    Third line there lists N numbers.
    See range details in describe.
  • 输出
  • Output the number of combinations in total.
  • 样例输入
  • 2
    5
    4
    1,2,3,4,5
    5
    6
    1,2,3,4,5
    
  • 样例输出
  • 2
    3
    
  • 提示
  • For the first case: 1+3=4, 4=4.
    For the second case: Don't consider the order, that means 1+2+3=6 equals 1+3+2=6 equals 3+1+2=6, etc.
  • 来源
  • Mr.Cai

题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1479

题解:简单的搜索题;

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int vis[100];
int num[100];
int n, k;
int ans;
void dfs(int pos,int sum)
{
	if(sum > k) return ;

	if(sum == k) {ans++; return;}


	for(int i = pos; i < n; i++)
	{
		if(!vis[i])
			vis[i] = 1;
		dfs(i+1,sum+num[i]);
		vis[i] = 0;
	}
}

int main ()
{
	int T;
	int i, j;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d %d",&n, &k);
		for(i = 0; i < n; i++)
		{
			scanf("%d", &num[i]);
			getchar();
		}
		sort(num,num+n);
		memset(vis, 0, sizeof(vis));
		ans = 0;
		dfs(0,0);
		printf("%d\n",ans);
	}
	return 0;
}




  • [F] 懒惰的风纪委Elaine

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • Elaine是学园都市中的一个风纪委,每天都会接到命令对某个街道进行检查,并抓捕危险分子。她所在的风纪委支部附近有M条街道。这些街道由北到南并排均匀的分布在一条直线上,每条街道之间的距离都为1。但是众所周知,Elaine是一个很懒很懒的人(-..-说我坏话!!被我看到了!!),她不想一步一步走完所有街道,但好在她的好友Kuso为她制作了大量的传送卷轴。不过,因为Kuso的能力等级太低,他制作的卷轴有严重的缺点,他的卷则只能向南飞一段固定的距离(当然,他预先制作了很多种类的卷轴),而Elaine所在的风纪委支部却在最北边。每一次出去检查,Elain都要使用好几张卷轴。但如果是某些不能传送到的地方,Elaine只能走过去了。不过回来的话,她就可以用自带的传送系统传送到支部的传送点。

    有一天,Elaine想知道,如果她从风纪委支部出发,可以检查那些街道。她手里有N种传送卷轴(1,2,3,,,N),每个卷轴可以传送的距离为Ai,卷轴的数量为Ci。则Elaine靠那些卷轴,可以不走路而直接传送的有哪些街道?

  • 输入
  • 数据有多组输入。每一组数据的第一行有两个数:N,M(0<N<=100,0<M<=1000)。分别表示传送卷轴的种类数和街道数量。在第二行有2N个数,A1,A2,A3...An,C1,C2,C3...Cn (1<=Ai<=100000,1<=Ci<=1000)。数据输入以0 0结束。
  • 输出
  • 每组数据占一行,输出一个数,为Elaine可以传送到的街道总数。
  • 样例输入
  • 3 10
    1 2 4 2 1 1
    2 5
    1 4 2 1
    0 0
  • 样例输出
  • 8
    4
  • 提示
  • 风纪委支部到第一条街道的距离也为1。
  • 来源
  • 本站或者转载
题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1480

题解(大神的解题报告):

  题目大意:给一些数字和这些数字的个数,问用这些数字可以组成多少个不同的数字。

  解题思路 多重背包中多次背包的优化 O(NC)
                
                  多次背包问题:给定 n 种物品和一个背包。第 i 种物品的价值是 Wi ,其体积
                                    为Vi,数量是Ki件,背包的容量为C。可以任意选择装入背包中的物品,
                                    求装入背包中物品的最大总价值。
                                    只不过这题中体积和价值相等了,是原命题的一个特例;
  
               对于第i件物品,设当前的数字为j,则有vis[j]|={ vis[j-vi*k]}(1<=k<=ki)
 
               (1).因为j是递增的,所以我们只要保存当vis[j-vi*k]==1且(j-vi*k)为最大就可以了,这样可以更有利于后面的j;
               (2).而我们每次增长可以采用j+=vi的方式来增长,且每次j的初始值可以取0~vi-1就可以了
                   这样j就取遍了所有的0~C区间,且时间复杂度也是O(C);
                   因为有n中物品,所以总的时间复杂度为O(N*C);
                   当价值和体积不相等的时候,我们只要把“保存当vis[j-vi*k]==1且(j-vi*k)为最大”
                   改成“维护一个价值(j-vi*k)的单调队列即可,队首为价值的最大值”;
 
  ps:如果这题你不是用严格O(N*C)的算法过的,建议去百度一下“多重背包中多次背包的优化O(NC)算法”,这个算法很高效,
          特别是在解决价值和体积不相等的情况。

大神的代码(供大家学习):
#include<stdio.h>
#include<string.h>

int dp[2][1010],A[110],C[110];
int main(){
    int n,m,i,j,k,g,ans,flag,sp;
    while(scanf("%d%d",&n,&m),n+m){
        memset(dp,0,sizeof(dp));dp[0][0]=1;
        for(i=1;i<=n;i++) scanf("%d",&A[i]);
        for(i=1;i<=n;i++) scanf("%d",&C[i]);
        flag=0;
        for(i=1;i<=n;i++){
            if(A[i]>m) continue;
            flag=1-flag;
            for(k=sp=0;k<=m;k++){
                dp[flag][k]=dp[1-flag][k];
                sp+=dp[1-flag][k];
            }
            if(sp==m+1) break;
            for(j=0;j<A[i];j++){
                if(j+A[i]>m) break;
                sp=dp[1-flag][j]?0:-10000;g=1;
                for(k=j+A[i];k<=m;k+=A[i],g++){
                    if(g-sp<=C[i])
                        dp[flag][k]=1;
                    if(dp[1-flag][k]) sp=g;
                }
            }
        }
        ans=0;
        for(i=1;i<=m;i++) ans+=dp[flag][i];
        printf("%d\n",ans);
    }
    return 0;
}





  • [G] Big Fibonacci

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • F0=0, F1=1, F2=1, F3=2, F4=3, F5=5…… 
    Fn = F(n-1) + F(n-2) ( n >= 2)
  • 输入
  • 输入一个整数n( 0 <= n <= 1,000,000,000)
  • 输出
  • 输出 Fn mod 20130719
  • 样例输入
  • 0
    1
    9
    1000000000
  • 样例输出
  • 0 
    1
    34
    2123828
  • 提示
  • 来源
  • 本站或者转载


题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1481

题解:第一种方法:用矩阵快速幂来求
          第二种方法:找循环节

//矩阵求解
#include<stdio.h>
#define N 20130719
struct matrix
{
	long long s[3][3];
};
matrix A,B,D;

matrix mul(matrix D, matrix A)
{
	int i, j, k;
	matrix s;
	for(i = 0; i < 2;  i++)
		for(j = 0; j < 2; j++)
		{
			s.s[i][j] = 0;
			for(k = 0; k < 2; k++)
				s.s[i][j] = (s.s[i][j] + D.s[i][k] * A.s[k][j]) % N;
		}

		return s;
}


matrix pow(matrix A, long long k)
{
	matrix s , D = A ;
	s.s[0][0] = 1;  s.s[0][1] = 0;
	s.s[1][0] = 0;  s.s[1][1] = 1;
	while(k)
	{
		if(k & 1)  s = mul(D, s);

		D = mul(D, D); 

		k = k/2;
	}

	return s;
}
int main (void)
{
	long long n;
	while(scanf("%lld", &n) != EOF)
	{
		if(n == 0) {printf("0\n");continue;}
		if(n == 1) {printf("1\n");continue;}
		A.s[0][0] = 1;  A.s[0][1] = 1;
		A.s[1][0] = 1;  A.s[1][1] = 0;

		B = pow(A,n);

		printf("%d\n",B.s[0][1]%N);
	}
	return 0;
}


//打表找出循环周期
#include<stdio.h>
int f[1000000];
int main()
{
	int a,b,c,i,j;
	f[0]=0;
	f[1]=1;
	for(i=2;i<=572889;i++)
	     f[i]=(f[i-1]+f[i-2])%20130719;
	while(scanf("%d",&a)!=EOF)
	{
		printf("%d\n",f[a%572880]);
	}
	return 0;
}



  • [H] 嘛~付钱吧!

  • 时间限制: 1000 ms 内存限制: 65535 K
  • 问题描述
  • 大白菜带着套套去超市买好吃的,到了付钱的时候,收银员告诉大白菜需要付N元。

    大白菜突然想知道自己付钱有多少种方法,求助我们聪明的ACMER来告诉他,有几种付钱的方法。

    (已知,我天朝有,红色老人头,绿色老人头,黄色老人头 (100,50,20,10,5,1 )等等,ps:当大白菜带着套套的时候,他将化身无敌大款,各种老人头要多少有多少);
  • 输入
  • 输入的N都是整数(N<=1000)
  • 输出
  • 对于每个输入,请给出大白菜付钱的方法数
  • 样例输入
  • 1
    5
    10
    
  • 样例输出
  • 1
    2
    4
  • 提示
  • 来源
  • 本站或者转载


题目链接:http://ac.nbutoj.com/Problem/view.xhtml?id=1482

题解:方法一:DP
          方法二:由于数据比较小,可以打表。

#include<stdio.h>
#include<string.h>
int dp[2005];
int a[6]={1,5,10,20,50,100};
int main()
{
	int i,j,k,n;
	memset(dp,0,sizeof(dp));
	dp[0]=1;
	for(i=0;i<6;i++)
	{
		for(j=a[i];j<=2000;j++)
			dp[j]+=dp[j-a[i]];
	}
	dp[0]=0;
	while(scanf("%d",&n)!=EOF)
	{
		printf("%d\n",dp[n]);
	}
	return 0;
}


#include<stdio.h>	
int n;
long long ans;
int pan (int x)
{
	if(x == n) 
		return 1;
	else if(x > n)
		return 0;
	else 
		return -1;

}
int main ()
{

	//freopen("data.txt","r",stdin);  
	//freopen("data.txt","w",stdout);  
	//for(n = 1; n <= 1000; n++)
	while(scanf("%d", &n) != EOF)
	{
		ans = 0;
		int sum ;
		int a, b, c, d, e, f;
		for(a = n / 100; a >= 0; a--)
		{
			sum = a * 100;
			if(pan(sum) == 1) {ans++;sum -= a * 100;continue;}
			else if(pan(sum) == 0) {sum = sum - a*100;continue;}

			for(b = (n - sum) / 50; b >= 0 ; b--)
			{
				sum += b * 50;
				if(pan(sum) == 1) {ans++;sum -= b * 50;continue;}
				else if(pan(sum) == 0) {sum -= b*50;continue;}

				for(c = (n - sum) / 20; c >= 0; c--)
				{
					sum += c * 20;
					if(pan(sum) == 1) {ans++;sum -= c * 20;continue;}
					else if(pan(sum) == 0) {sum -= c*20;continue;}

					for(d = (n - sum)/10; d >= 0; d--)
					{
						sum += d * 10;
						if(pan(sum) == 1) {ans++;sum -= d * 10;continue;}
						else if(pan(sum) == 0) {sum -= d*10;continue;}

						for(e = (n - sum)/5; e >= 0; e--)
						{
							sum += e * 5;
							if(pan(sum) == 1) {ans++;sum -= e * 5;continue;}
							else if(pan(sum) == 0) {sum -= e * 5;continue;}


							ans++;
							if(e != 0)
								sum -= e * 5;
							else if(d != 0)
								sum -= d * 10;
							else if(c != 0)
								sum -= c * 20;
							else if(b != 0)
								sum -= b * 50;
							else if(a != 0)
								sum -= a * 100;
						}
					}
				}
			}
		} 

		printf("%lld\n", ans);
	}
	return 0;
}




你可能感兴趣的:(2013.7.19 2013NBUT暑期集训成果赛)