算法-藍橋杯習題(3-2)

 

藍橋杯習題
 

目錄

 

入門訓練(詳見 算法-藍橋杯習題(1-1)Go

 

基礎練習(詳見 算法-藍橋杯習題(2-1)Go

基礎練習(詳見 算法-藍橋杯習題(2-2)Go

算法訓練(詳見 算法-藍橋杯習題(3-1)Go

算法訓練(詳見 算法-藍橋杯習題(3-2)Go

算法訓練(詳見 算法-藍橋杯習題(3-3)Go

算法訓練(詳見 算法-藍橋杯習題(3-4)Go

算法訓練(詳見 算法-藍橋杯習題(3-5)Go

算法訓練(詳見 算法-藍橋杯習題(3-6)Go

算法提高(詳見 算法-藍橋杯習題(4-1)Go

算法提高(詳見 算法-藍橋杯習題(4-2)Go

算法提高(詳見 算法-藍橋杯習題(4-3)Go

 

歷屆試題(詳見 算法-藍橋杯習題(5-1)Go

 

歷屆試題(詳見 算法-藍橋杯習題(5-2)Go

 

藍橋杯練習系統評測數據

鏈接: https://pan.baidu.com/s/1brjjmwv
密碼: iieq

 

算法訓練(PartB-20題)

 

/*
算法訓練 裝箱問題

問題描述
  有一個箱子容量爲V(正整數,0<=V<=20000),同時有n個物品(0<n<=30),每個物品有一個體積(正整數)。
  要求n個物品中,任取若干個裝入箱內,使箱子的剩餘空間爲最小。
輸入格式
  第一行爲一個整數,表示箱子容量;
  第二行爲一個整數,表示有n個物品;
  接下來n行,每行一個整數表示這n個物品的各自體積。
輸出格式
  一個整數,表示箱子剩餘空間。
  樣例輸入
  24
  6
  8
  3
  12
  7
  9
  7
樣例輸出
0
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>


#define V 20001
#define N 30
main()
{
    int f[V];
    int i,j;
    int n,v;
    int T[V];
        
    memset(f,0,sizeof(f));
    memset(T,0,sizeof(T));
    scanf("%d%d",&n,&v);
    for(i=1;i<=v;i++)
        scanf("%d",&f[i]);
    
    for(i=1;i<=v;i++)
    {    
        for(j=n;j>=f[i];j--)
        {
            if(f[i]<=j&&f[i]+T[j-f[i]]>T[j])
                T[j] = f[i]+T[j-f[i]];  
        }
     }
     printf("%d\n",n-T[n]);
    
 
  
    return 0;
    
}

 

 

 

 

 

 

/*
算法訓練 數的劃分

問題描述
  將整數n分成k份,且每份不能爲空,任意兩份不能相同(不考慮順序)。
  例如:n=7,k=3,下面三種分法被認爲是相同的。
  1,1,5; 1,5,1; 5,1,1;
  問有多少種不同的分法。
輸入格式
  n,k
輸出格式
  一個整數,即不同的分法
樣例輸入
7 3
樣例輸出
4 {四種分法爲:1,1,5;1,2,4;1,3,3;2,2,3;}
數據規模和約定
  6<n<=200,2<=k<=6
*/
#include <stdio.h>
int main()
{
    int i,j,n,k,a[201][7]={0};
    a[1][1] = 1;
    scanf("%d%d",&n,&k);
    for (i = 2; i <= n; i++)
        for (j = 1; j <= k; j++)
            if (i >= j)
                a[i][j] = a[i - j][j] + a[i - 1][j - 1];
    printf("%d",a[n][k]);
    return 0;
}

 

 

/*
算法訓練 一元三次方程求解

問題描述
  有形如:ax3+bx2+cx+d=0 這樣的一個一元三次方程。給出該方程中各項的係數(a,b,c,d 均爲實數),並約定該方程存在三個不同實根(根的範圍在-100至100之間),且根與根之差的絕對值>=1。要求三個實根。。
輸入格式
  四個實數:a,b,c,d
輸出格式
  由小到大依次在同一行輸出這三個實根(根與根之間留有空格),並精確到小數點後2位
樣例輸入
1 -5 -4 20
樣例輸出
-2.00 2.00 5.00
數據規模和約定
  |a|,|b|,|c|,|d|<=10
*/
#include<stdio.h>
float a,b,c,d,x=-100,t,y;
float f(float z)
{
	return a*z*z*z+b*z*z+c*z+d;
}
int main()
{
	scanf("%f%f%f%f",&a,&b,&c,&d);
	t=f(x);
	while(x<=100)
	{
		y=f(x);
		if(y*t<=0)printf("%.2f ",x);
		x=x+0.001;t=y;
	}
	printf("\n");
	return 0;
}

 

 

 

 

 

 

 

/*
算法訓練 統計單詞個數

問題描述
  給出一個長度不超過200的由小寫英文字母組成的字母串(約定;該字串以每行20個字母的方式輸入,且保證每行一定爲20個)。要求將此字母串分成k份 (1<k<=40),且每份中包含的單詞個數加起來總數最大(每份中包含的單詞可以部分重疊。當選用一個單詞之後,其第一個字母不能再用。例 如字符串this中可包含this和is,選用this之後就不能包含th)。
  單詞在給出的一個不超過6個單詞的字典中。
  要求輸出最大的個數。
輸入格式
  第一行有二個正整數(p,k)
  p表示字串的行數;
  k表示分爲k個部分。
  接下來的p行,每行均有20個字符。
  再接下來有一個正整數s,表示字典中單詞個數。(1<=s<=6)
  接下來的s行,每行均有一個單詞。
輸出格式
  每行一個整數,分別對應每組測試數據的相應結果。
樣例輸入
1 3
thisisabookyouareaoh
4
is
a
ok
sab
樣例輸出
7
數據規模和約定
  長度不超過200,1<k<=40,字典中的單詞數不超過6。
*/
#include <stdio.h>
#include <string.h>
#define InfiniteMin -999999999

int p,k,s;
char str[10][21];
char word[6][16];
int flag[6];
int Cnt[201][201];

int getCnt(int a,int b)
{
    int i,j,m,count=0;
    if(Cnt[a][b]<=0)
    {
        for (i=a;i<=b;i++)
            for (j=0;j<s;j++)
        	    if(word[j][0]==str[i/20][i%20])
        	    {
        	        for(m=1;word[j][m]!='\0'&&i+m<=b;m++)
        		        if(word[j][m]!=str[(i+m)/20][(i+m)%20])
					        break;
        	        if(word[j][m]=='\0')
        	        {
        	            count++;
        	            break; 
					}
        	    }
        Cnt[a][b]=count;
    }
    return Cnt[a][b];
}

int main()
{
	int i,u,K;
	int max,temp;
	int F[201][41];
	scanf("%d%d",&p,&k);
	for(i=0;i<p;i++)
	     scanf("%s",str[i]);
	scanf("%d",&s);
	for(i=0;i<s;i++)
	    scanf("%s",word[i]);
	for(i=0;i<20*p;i++)
	    F[i][1]=getCnt(0,i);
	for(K=2;K<=k;K++)
	    for(i=k;i<p*20;i++)
	    {
			max=InfiniteMin;
			for(u=K-1;u<=i-1;u++)
			{
				temp=F[u][K-1]+getCnt(u+1,i);
			    max=max>temp?max:temp;
			}
			F[i][K]=max;
	    }
	printf("%d",F[p*20-1][k]);
	return 0;
}

 

 

 

 

 

 

 

/*
算法訓練 Car的旅行路線

問題描述
  又到暑假了,住在城市A的Car想和朋友一起去城市B旅遊。她知道每個城市都有四個飛機場,分別位於一個矩形的四個頂點上,同一個城市中兩個機場之間有一 條筆直的高速鐵路,第I個城市中高速鐵路了的單位里程價格爲Ti,任意兩個不同城市的機場之間均有航線,所有航線單位里程的價格均爲t。
  那麼Car應如何安排到城市B的路線才能儘可能的節省花費呢?她發現這並不是一個簡單的問題,於是她來向你請教。
  找出一條從城市A到B的旅遊路線,出發和到達城市中的機場可以任意選取,要求總的花費最少。
輸入格式
  的第一行有四個正整數s,t,A,B。
  S表示城市的個數,t表示飛機單位里程的價格,A,B分別爲城市A,B的序號,(1<=A,B<=S)。
  接下來有S行,其中第I行均有7個正整數xi1,yi1,xi2,yi2,xi3,yi3,Ti,這當中的(xi1,yi1),(xi2,yi2),(xi3,yi3)分別是第I個城市中任意三個機場的座標,T I爲第I個城市高速鐵路單位里程的價格。
輸出格式
  共有n行,每行一個數據對應測試數據,保留一位小數。
樣例輸入
1
1 10 1 3
1 1 1 3 3 1 30
2 5 7 4 5 2 1
8 6 8 8 11 6 3
樣例輸出
47.55
數據規模和約定
  0<S<=100,
*/
//C++
#include <float.h> 
#include <math.h> 
#include <stdio.h> 
#include<cstring>
#define Sqr(x) ((x)*(x)) 

double s[400][400], x[400], y[400]; 

int main() 
{ 
    int a, b, n, tt,t,i,j,k,N; 
		double res = DBL_MAX; 
		scanf("%d%d%d%d", &n, &tt, &a, &b); 
		a-=1; 
		b-=1; 
		for ( i = 0; i < n; ++i) 
		{ 
			for ( j = 0; j < 3; ++j) 
				scanf("%lf%lf", &x[i*4+j], &y[i*4+j]); 
			scanf("%d", &t); 
			for (j = 0; j < 3; ++j) 
			{ 
				double x1 = x[i*4+(j+1)%3], y1 = y[i*4+(j+1)%3], 
					x2 = x[i*4+(j+2)%3], y2 = y[i*4+(j+2)%3]; 
				if (fabs((x[i*4+j]-x1)*(x[i*4+j]-x2)+(y[i*4+j]-y1)*(y[i*4+j]-y2)) < 1E-7) 
					x[i*4+3] = x1+x2-x[i*4+j], y[i*4+3] = y1+y2-y[i*4+j]; 
			} 
			for ( j = 0; j < 4; ++j) 
				for (k = 0; k < 4; ++k) 
					s[i*4+j][i*4+k] = t*sqrt(Sqr(x[i*4+j]-x[i*4+k])+Sqr(y[i*4+j]-y[i*4+k])); 
			for ( j = 0; j < i*4; ++j) 
				for ( k = 0; k < 4; ++k) 
					s[i*4+k][j] = tt*sqrt(Sqr(x[i*4+k]-x[j])+Sqr(y[i*4+k]-y[j])), 
					s[j][i*4+k] = s[i*4+k][j]; 
		} 
		for ( k = 0; k < n*4; ++k) 
			for (i = 0; i < n*4; ++i) 
				for ( j = 0; j < n*4; ++j) 
					if (s[i][k]+s[k][j] < s[i][j]) 
						s[i][j] = s[i][k]+s[k][j]; 
		for (i = 0; i < 4; ++i) 
			for ( j = 0; j < 4; ++j) 
				if (s[a*4+i][b*4+j] < res) 
					res = s[a*4+i][b*4+j]; 
		printf("%.1lf\n", res); 
	return 0;
}

 

 

 

 

 

 

/*
  算法訓練 麥森數 
  
  問題描述
  形如2P-1的素數稱爲麥森數,這時P一定也是個素數。但反過來不一定,即如果P是個素數,2P-1不一定也是素數。到1998年底,人們已找到了37個麥森數。最大的一個是P=3021377,它有909526位。麥森數有許多重要應用,它與完全數密切相關。
  任務:從文件中輸入P(1000<P<3100000),計算2P-1的位數和最後500位數字(用十進制高精度數表示)
輸入格式
  文件中只包含一個整數P(1000<P<3100000)
輸出格式
  第一行:十進制高精度數2P-1的位數。
  第2-11行:十進制高精度數2P-1的最後500位數字。(每行輸出50位,共輸出10行,不足500位時高位補0)
  不必驗證2P-1與P是否爲素數。
樣例輸入
1279
樣例輸出
386
00000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000
00000000000000104079321946643990819252403273640855
38615262247266704805319112350403608059673360298012
23944173232418484242161395428100779138356624832346
49081399066056773207629241295093892203457731833496
61583550472959420547689811211693677147548478866962
50138443826029173234888531116082853841658502825560
46662248318909188018470682222031405210266984354887
32958028878050869736186900714720710555703168729087
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#define LEN 125  //每數組元素存放十進制的4位,因此數組最多隻要125個元素即可
//Multiply函數功能是計算高精度乘法a*b,結果的末500位放在a中

void Multiply(int *a,int *b)
{
	int i,j;
	int nCarry; //存放進位
	int nTmp;
	int c[LEN];  //存放結果的末500位
	memset(c,0,sizeof(int)*LEN);
	for(i=0;i<LEN;i++)
	{
		nCarry=0;
		for(j=0;j<LEN-i;j++)
		{
			nTmp=c[i+j]+a[j]*b[i]+nCarry;
			c[i+j]=nTmp%10000;
			nCarry=nTmp/10000;
		}
	}
	memcpy(a,c,LEN*sizeof(int));
}

int main()
{
	int i,p;
	int anPow[LEN];  //存放不斷增長的2的次冪
	int aResult[LEN];  //存放最終結果的末500位
	scanf("%d",&p);
	printf("%d\n",(int)(p*log10(2))+1);
	//下面將2的次冪初始化爲2^(2^0)(a^b表示a的b次方),最終結果初始化爲1
	anPow[0]=2;
	aResult[0]=1;
	for(i=1;i<LEN;i++)
	{
		anPow[i]=0;
		aResult[i]=0;
	}
	//下面計算2的p次方
	while(p>0)  //p=0則說明p中的有效位都用過了,不需要再計算下去
	{
		if(p&1)  //判斷此時p中最低位是否爲1
			Multiply(aResult,anPow);
		p>>=1;
		Multiply(anPow,anPow);
	}
	aResult[0]--;  //2的p次方算出後減1

	//輸出結果
	for(i=LEN-1;i>=0;i--)
	{
		if(i%25==12)
			printf("%02d\n%02d",aResult[i]/100,aResult[i]%100);
		else
		{
			printf("%04d",aResult[i]);
			if(i%25==0)
				printf("\n");
		}
	}
	return 0;
}

 

 

 

 

 

 

/*
算法訓練 FBI樹

問題描述
  我們可以把由“0”和“1”組成的字符串分爲三類:全“0”串稱爲B串,全“1”串稱爲I串,既含“0”又含“1”的串則稱爲F串。
  FBI樹是一種二叉樹,它的結點類型也包括F結點,B結點和I結點三種。由一個長度爲2N的“01”串S可以構造出一棵FBI樹T,遞歸的構造方法如下:
  1)T的根結點爲R,其類型與串S的類型相同;
  2)若串S的長度大於1,將串S從中間分開,分爲等長的左右子串S1和S2;由左子串S1構造R的左子樹T1,由右子串S2構造R的右子樹T2。
  現在給定一個長度爲2N的“01”串,請用上述構造方法構造出一棵FBI樹,並輸出它的後序遍歷序列。
輸入格式
  第一行是一個整數N(0 <= N <= 10),第二行是一個長度爲2N的“01”串。
輸出格式
  包括一行,這一行只包含一個字符串,即FBI樹的後序遍歷序列。
樣例輸入
3
10001011
樣例輸出
IBFBBBFIBFIIIFF
數據規模和約定
  對於40%的數據,N <= 2;
  對於全部的數據,N <= 10。
  注:
  [1] 二叉樹:二叉樹是結點的有限集合,這個集合或爲空集,或由一個根結點和兩棵不相交的二叉樹組成。這兩棵不相交的二叉樹分別稱爲這個根結點的左子樹和右子樹。
  [2] 後序遍歷:後序遍歷是深度優先遍歷二叉樹的一種方法,它的遞歸定義是:先後序遍歷左子樹,再後序遍歷右子樹,最後訪問根。
*/
#include <stdio.h>
#include <malloc.h>

char in[1025];

typedef struct node
{
	char data;
	struct node *lchild,*rchild;
}Binode;

Binode *create(int a,int b)
{
	char ch;
	Binode *p;
	int i,count0=0,count1=0,flag=0;
//	scanf("%c",&ch);
//	getchar();
	if(a==b)
	{
		(in[a]=='0')?(ch='B'):(ch='I');
		p=(Binode *)malloc(sizeof(Binode));
		p->data=ch;
		p->lchild=NULL;
		p->rchild=NULL;
		return p;
	}
	for(i=a;i<=b;i++)
	{
		(in[i]=='0')?(count0++):(count1++);
		if(count0 && count1)
		{
			ch='F';
			flag=1;
			break;
		}
	}
	if(!flag)
	{
		if(!count0)
			ch='I';
		else
			ch='B';
	}
	p=(Binode *)malloc(sizeof(Binode));
	p->data=ch;
	p->lchild=create(a,((b+1)-a)/2+a-1);
	p->rchild=create(((b+1)-a)/2+a,b);
	return p;
}

void display(Binode *p)
{
	if(!p)
		return;
	display(p->lchild);
	display(p->rchild);
	printf("%c",p->data);
}

int main()
{
	Binode *r;
	char c;
	int p=0,n;
	scanf("%d",&n);
	getchar();
	while(1)
	{
		c=getchar();
		if(c=='\n')
			break;
		in[p++]=c;
	}
	r=create(0,p-1);
	display(r);

	return 0;
}

 

 

 

 

 

 

/*
算法訓練 星際交流


問題描述
  人類終於登上了火星的土地並且見到了神祕的火星人。人類和火星人都無法理解對方的語言,但是我們的科學家發明了一種用數字交流的方法。這種交流方法是這樣 的,首先,火星人把一個非常大的數字告訴人類科學家,科學家破解這個數字的含義後,再把一個很小的數字加到這個大數上面,把結果告訴火星人,作爲人類的回 答。
  火星人用一種非常簡單的方式來表示數字——掰手指。火星人只有一隻手,但這隻手上有成千上萬的手指,這些手指排成一列,分別編號爲1,2,3……。火星人的任意兩根手指都能隨意交換位置,他們就是通過這方法計數的。
  一個火星人用一個人類的手演示瞭如何用手指計數。如果把五根手指——拇指、食指、中指、無名指和小指分別編號爲1,2,3,4和5,當它們按正常順序排列 時,形成了5位數12345,當你交換無名指和小指的位置時,會形成5位數12354,當你把五個手指的順序完全顛倒時,會形成54321,在所有能夠形 成的120個5位數中,12345最小,它表示1;12354第二小,它表示2;54321最大,它表示120。下表展示了只有3根手指時能夠形成的6個 3位數和它們代表的數字:
  三進制數
  123
  132
  213
  231
  312
  321
  代表的數字
  1
  2
  3
  4
  5
  6
  現在你有幸成爲了第一個和火星人交流的地球人。一個火星人會讓你看他的手指,科學家會告訴你要加上去的很小的數。你的任務是,把火星人用手指表示的數與科 學家告訴你的數相加,並根據相加的結果改變火星人手指的排列順序。輸入數據保證這個結果不會超出火星人手指能表示的範圍。
輸入格式
  包括三行,第一行有一個正整數N,表示火星人手指的數目(1 <= N <= 10000)。第二行是一個正整數M,表示要加上去的小整數(1 <= M <= 100)。下一行是1到N這N個整數的一個排列,用空格隔開,表示火星人手指的排列順序。
輸出格式
  只有一行,這一行含有N個整數,表示改變後的火星人手指的排列順序。每兩個相鄰的數中間用一個空格分開,不能有多餘的空格。
樣例輸入
5
3
1 2 3 4 5
樣例輸出
1 2 4 5 3
數據規模和約定
  對於30%的數據,N<=15;
  對於60%的數據,N<=50;
  對於全部的數據,N<=10000;
*/
#include<stdio.h>
int main()
{
    int n,w,i1,i2;
    int a[100000];
	scanf("%d%d",&n,&w);
	for(i1=0;i1<n;i1++)
         scanf("%d",&a[i1]);
	while(w--)
    {   int max=a[n-1],min;
    	
		for(i1=n-2;i1>=0;i1--)
    	{
	        if(max<a[i1])
	             {max=a[i1];
				 continue;
	             }
	        min=max; 
			int xiabiao=-1;    
			for(i2=i1+1;i2<=n-1;i2++)
			{
				if(a[i2]>a[i1])
				  {
  					  if(min>=a[i2])
  					  {
  					  	min=a[i2];
  					  	xiabiao=i2;
  					  }
  				}
			}	
			int k=a[i1];
			a[i1]=min;
			a[xiabiao]=k;
			for(i2=1;i1+i2<n-i2;i2++)
			{
				int kp=a[i2+i1];
				a[i2+i1]=a[n-i2];
				a[n-i2]=kp;
			}			
		   break;
	    }
    	    
    }
for(i1=0;i1<n;i1++)
       printf("%d ",a[i1]);   
       printf("\n"); 
	return 0;
}

 

 

 

 

 

 

/*
算法訓練 校門外的樹

問題描述
  某校大門外長度爲L的馬路上有一排樹,每兩棵相鄰的樹之間的間隔都是1米。我們可以把馬路看成一個數軸,馬路的一端在數軸0的位置,另一端在L的位置;數 軸上的每個整數點,即0,1,2,……,L,都種有一棵樹。
  由於馬路上有一些區域要用來建地鐵。這些區域用它們在數軸上的起始點和終止點表示。已 知任一區域的起始點和終止點的座標都是整數,區域之間可能有重合的部分。現在要把這些區域中的樹(包括區域端點處的兩棵樹)移走。你的任務是計算將這些樹 都移走後,馬路上還有多少棵樹。
輸入格式
  輸入文件的第一行有兩個整數L(1 <= L <= 10000)和 M(1 <= M <= 100),L代表馬路的長度,M代表區域的數目,L和M之間用一個空格隔開。接下來的M行每行包含兩個不同的整數,用一個空格隔開,表示一個區域的起始點 和終止點的座標。
輸出格式
  輸出文件包括一行,這一行只包含一個整數,表示馬路上剩餘的樹的數目。
樣例輸入
500 3
150 300
100 200
470 471
樣例輸出
298
數據規模和約定
  對於20%的數據,區域之間沒有重合的部分;
  對於其它的數據,區域之間有重合的情況。
*/
#include<stdio.h>
typedef struct
{
int start;
int end;
int flag;
}extent;
int main()
{
int L,M,i,j;
extent e[101];
scanf("%d%d",&L,&M);
for(i=1;i<=M;i++)
{
scanf("%d%d",&(e[i].start),&(e[i].end));
e[i].flag=1;
for(j=1;j<i;j++)
{
if(!(e[i].end<e[j].start||e[i].start>e[j].end)&&e[j].flag)
{
e[j].flag=0;
if(e[i].start>e[j].start)
e[i].start=e[j].start;
if(e[i].end<e[j].end)
e[i].end=e[j].end;
}
}//調整區間 
}
for(i=1;i<=M;i++)
if(e[i].flag)
L=L-(e[i].end-e[i].start+1);
printf("%d",L+1);
return 0;
}

 

/*
算法訓練 入學考試

問題描述
  辰辰是個天資聰穎的孩子,他的夢想是成爲世界上最偉大的醫師。爲此,他想拜附近最有威望的醫師爲師。醫師爲了判斷他的資質,給他出了一個難題。醫師把他帶到一個到處都是草藥的山洞裏對他說:“孩子,這個山洞裏有一些不同的草藥,採每一株都需要一些時間,每一株也有它自身的價值。我會給你一段時間,在這段時間裏,你可以採到一些草藥。如果你是一個聰明的孩子,你應該可以讓採到的草藥的總價值最大。”
  如果你是辰辰,你能完成這個任務嗎?
輸入格式
  第一行有兩個整數T(1 <= T <= 1000)和M(1 <= M <= 100),用一個空格隔開,T代表總共能夠用來採藥的時間,M代表山洞裏的草藥的數目。接下來的M行每行包括兩個在1到100之間(包括1和100)的整數,分別表示採摘某株草藥的時間和這株草藥的價值。
輸出格式
  包括一行,這一行只包含一個整數,表示在規定的時間內,可以採到的草藥的最大總價值。
樣例輸入
70 3
71 100
69 1
1 2
樣例輸出
3
數據規模和約定
  對於30%的數據,M <= 10;
  對於全部的數據,M <= 100。
**/
#include <stdio.h>

int totalTime, medics;
int value[100];
int time[100];
int max[1001];

int main() 
{
    scanf("%d%d", &totalTime, &medics);
    int i, t;
    for (i=0; i<medics; i++)
    {
        scanf("%d%d", &time[i], &value[i]);
    }

    for (i=0; i<medics; i++)
    {
        for (t=totalTime; t>0; t--)
        {
            if (time[i] <= t)
            {
                if (value[i] + max[t-time[i]] > max[t])//第i個的價值+不選第i個且用時爲t-time[i-1]時最大價值
                {
                    max[t] = value[i] + max[t-time[i]];
                }
            }
        }
    }
    printf("%d\n", max[totalTime]);
    return 0;
}

 

 

/*
算法訓練 開心的金明

問題描述
  金明今天很開心,家裏購置的新房就要領鑰匙了,新房裏有一間他自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎 麼佈置,你說了算,只要不超過N元錢就行”。今天一早金明就開始做預算,但是他想買的東西太多了,肯定會超過媽媽限定的N元。於是,他把每件物品規定了一 個重要度,分爲5等:用整數1~5表示,第5等最重要。他還從因特網上查到了每件物品的價格(都是整數元)。他希望在不超過N元(可以等於N元)的前提 下,使每件物品的價格與重要度的乘積的總和最大。
  設第j件物品的價格爲v[j],重要度爲w[j],共選中了k件物品,編號依次爲 j1,j2,……,jk,則所求的總和爲:
  v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*爲乘號)
  請 你幫助金明設計一個滿足要求的購物單。
輸入格式
  輸入文件 的第1行,爲兩個正整數,用一個空格隔開:
  N m
  (其中N(<30000)表示總錢 數,m(<25)爲希望購買物品的個數。)
  從第2行到第m+1行,第j行給出了編號爲j-1的物品的基本數據,每行有2個非負整數
  v p
  (其中v表示該物品的價格(v<=10000),p表示該物品的重要度(1~5))
輸出格式
  輸出文件只有一個正整數,爲不超過總錢數的物品的價格與重要度乘積的總和的最大值(<100000000)。
樣例輸入
1000 5
800 2
400 5
300 5
400 3
200 2
樣例輸出
3900
數據規模和約定
*/
#include <stdio.h>
long mnum[30000]; 
long objprice[25]; //第i件商品的價格 
long objk[25];     //第i件商品的價格與權值的積 
int M,N;    //M表示金錢總數,N表示商品總數 
int main()
{
	int i,m;
	int maxp = 0;
	scanf("%u%u", &M, &N);   //M表示金錢總數,N表示商品總數 
	for(i=0;i<N;++i) //依次輸入第i件商品的價格與權值,並計算出商品的價格與權值之積 
	{
		scanf("%u%u", &objprice[i], &m);
		objk[i] = m*objprice[i];
	}
	for(i=0;i<N;++i)  
	{
		for(m=0;m<M-objprice[i];++m)
			if (mnum[m+objprice[i]] + objk[i] > mnum[m])
				mnum[m] = mnum[m+objprice[i]] + objk[i];
	}	
	for(i=0;i<M;++i) 
		if (mnum[i] > maxp)
			maxp = mnum[i];
	printf("%u", maxp);
	return 0;
}

 

 

/*
算法訓練 JAM計數法

問題描述
  Jam是個喜歡標新立異的科學怪人。他不使用阿拉伯數字計數,而是使用小寫英文字母計數,他覺得這樣做,會使世界更加豐富多彩。在他的計數法中,每個數字的位數都是相同的(使用相同個數的字母),英文字母按原先的順序,排在前面的字母小於排在它後面的字母。我們把這樣的“數字”稱爲Jam數字。在Jam數字中,每個字母互不相同,而且從左到右是嚴格遞增的。每次,Jam還指定使用字母的範圍,例如,從2到10,表示只能使用{b,c,d,e,f,g,h,i,j}這些字母。如果再規定位數爲5,那麼,緊接在Jam數字“bdfij”之後的數字應該是“bdghi”。(如果我們用U、V依次表示Jam數字“bdfij”與“bdghi”,則U<V< span>,且不存在Jam數字P,使U<P<V< span>)。你的任務是:對於從文件讀入的一個Jam數字,按順序輸出緊接在後面的5個Jam數字,如果後面沒有那麼多Jam數字,那麼有幾個就輸出幾個。
輸入格式
  有2行,第1行爲3個正整數,用一個空格隔開:
  s t w
  (其中s爲所使用的最小的字母的序號,t爲所使用的最大的字母的序號。w爲數字的位數,這3個數滿足:1≤s<T≤26, 2≤w≤t-s )
  第2行爲具有w個小寫字母的字符串,爲一個符合要求的Jam數字。
  所給的數據都是正確的,不必驗證。
輸出格式
  最多爲5行,爲緊接在輸入的Jam數字後面的5個Jam數字,如果後面沒有那麼多Jam數字,那麼有幾個就輸出幾個。每行只輸出一個Jam數字,是由w個小寫字母組成的字符串,不要有多餘的空格。
樣例輸入
2 10 5
bdfij
樣例輸出
bdghi
bdghj
bdgij
bdhij
befgh
*/
#include <stdio.h>

int main()
{
	int s,t,w;
	char in[26];
	int ad[26],i,j,k,flag,st;

	scanf("%d%d%d",&s,&t,&w);
	getchar();
	for(i=0;i<w;i++)
	{
		scanf("%c",&in[i]);
		ad[i]=in[i]-'a'+1;
	}
//	for(i=0;i<w;i++)
//		printf("%d ",ad[i]);//+'a'-1);
//	printf("\n");
	for(i=0;i<5;i++)
	{
		if(ad[0]==(t-w+1))
			break;
	//	flag=0;
		for(j=w-1;j>=0;j--)
		{
			if(ad[j]==t-w+j+1)
				continue;
	//		if(!flag)
	//		{
			//	ad[j]++;
				st=++ad[j];
			//	k=j;
				for(k=j+1;k<w;k++)
					ad[k]=++st;
				for(k=0;k<w;k++)
					printf("%c",ad[k]+'a'-1);
				printf("\n");
				flag=1;
				break;
	//		}
		}
	}

	return 0;
}


 

/*
算法訓練 數列

問題描述
  給定一個正整數k(3≤k≤15),把所有k的方冪及所有有限個互不相等的k的方冪之和構成一個遞增的序列,例如,當k=3時,這個序列是:
  1,3,4,9,10,12,13,…
  (該序列實際上就是:30,31,30+31,32,30+32,31+32,30+31+32,…)
  請你求出這個序列的第N項的值(用10進制數表示)。
  例如,對於k=3,N=100,正確答案應該是981。
輸入格式
  只有1行,爲2個正整數,用一個空格隔開:
  k N
  (k、N的含義與上述的問題描述一致,且3≤k≤15,10≤N≤1000)。
輸出格式
  計算結果,是一個正整數(在所有的測試數據中,結果均不超過2.1*109)。(整數前不要有空格和其他符號)。
樣例輸入
3 100
樣例輸出
981

*/
#include <stdio.h>

int main()
{
	int re[1000],k,n;
	int p=0,t=1,i,j,f;

	re[0]=1;j=0,f=1;
	scanf("%d%d",&k,&n);
	for(i=1;i<n;i++)
	{
		if(f==re[j])
		{
			f*=k;
			re[i]=f;
//			printf("%d ",f);
			j=0;
			continue;
		}
		t=f+re[j++];
		re[i]=t;
//		printf("%d ",t);
	}
	printf("%d",re[i-1]);

	return 0;
}

 

/*
算法訓練 紀念品分組

問題描述
  元旦快到了,校學生會讓樂樂負責新年晚會的紀念品發放工作。爲使得參加晚會的同學所獲得的紀念品價值 相對均衡,他要把購來的紀念品根據價格進行分組,但每組最多隻能包括兩件紀念品,並且每組紀念品的價格之和不能超過一個給定的整數。爲了保證在儘量短的時 間內發完所有紀念品,樂樂希望分組的數目最少。
  你的任務是寫一個程序,找出所有分組方案中分組數最少的一種,輸出最少的分組數目。
輸入格式
  輸入包含n+2行:
  第1行包括一個整數w,爲每組紀念品價格之和的上限。
  第2行爲一個整數n,表示購來的紀念品的總件數。
  第3~n+2行每行包含一個正整數pi (5 <= pi <= w),表示所對應紀念品的價格。
輸出格式
  輸出僅一行,包含一個整數,即最少的分組數目。
樣例輸入
100
9
90
20
20
30
50
60
70
80
90
樣例輸出
6
數據規模和約定
  50%的數據滿足:1 <= n <= 15
  100%的數據滿足:1 <= n <= 30000, 80 <= w <= 200
*/
#include <stdio.h>
void qsort(int i,int j);
	int a[30000];
	void qsort(int i,int j){     
	int x,p,q;    
	x=a[i]; p=i; q=j;    
	while (i<j)     
	{           
		while ((i<j)&&(a[j]>x))
		j--;         
		if (i<j)          
		{             
			a[i]=a[j];             
			i++;          
		}   
		        
		while ((i<j)&&(a[i]<x))
		i++;
	         
		if (i<j)           
		{              
			a[j]=a[i];              
			j--;          
		}     
	}     
	a[i]=x;     
	if (p<i-1) 
	qsort(p,i-1);     
	if (i+1<q) 
	qsort(i+1,q);
}
main(){      
	int n,w,s,i,j;   
	  
	scanf("%d%d",&w,&n);
	     
	for (i=0;i<n;i++) 
	scanf("%d",&a[i]);
	      
	qsort(0,n-1);      
	i=0;
	j=n-1; 
	s=0;  
	   
	while (i<j)     
	{           
		s++;           
		if (a[i]+a[j]<=w)           
		{              
		i++;              
		j--;           
		}            
		else 
		j--;     
	} 
	     
	if ((i==j)&&(a[i]<=w)) 
	s++;       
	printf("%d",s);     
	getchar();      
	getchar();
	     
	return(0);
}

 

/*
算法訓練 傳球遊戲

 【問題描述】
  上體育課的時候,小蠻的老師經常帶着同學們一起做遊戲。這次,老師帶着同學們一起做傳球遊戲。
  遊戲規則是這樣的:n個同學站成一個圓圈,其中的一個同學手裏拿着一個球,當老師吹哨子時開始傳球,每個同學可以把球傳給自己左右的兩個同學中的一個(左右任意),當老師再次吹哨子時,傳球停止,此時,拿着球沒傳出去的那個同學就是敗者,要給大家表演一個節目。
  聰明的小蠻提出一個有趣的問題:有多少種不同的傳球方法可以使得從小蠻手裏開始傳的球,傳了m次以後,又回到小蠻手裏。兩種傳球的方法被視作不同的方法,當且僅當這兩種方法中,接到球的同學按接球順序組成的序列是不同的。比如有3個同學1號、2號、3號,並假設小蠻爲1號,球傳了3次回到小蠻手裏的方式有1->2->3->1和1->3->2->1,共2種。
輸入格式
  共一行,有兩個用空格隔開的整數n,m(3<=n<=30,1<=m<=30)。
輸出格式
  t共一行,有一個整數,表示符合題意的方法數。
樣例輸入
3 3
樣例輸出
2
數據規模和約定
  40%的數據滿足:3<=n<=30,1<=m<=20
  100%的數據滿足:3<=n<=30,1<=m<=30
*/
#include <stdio.h> 
int n,m;
int at1(int x)
{
	if(x<1) return x + n;   
	if(x>n) return x - n;  
	return x;
}int main()
{
	int f[31][31] = {0},i,j;  
	scanf("%d%d",&n,&m);   
	f[1][2] = f[1][n] = 1; 
	for(i = 2;i <= m;i++)    
	for(j = 1;j <= n;j++)
		f[i][j] = f[i - 1][at1(j - 1)] + f[i - 1][at1(j + 1)]; 
	printf("%d",f[m][1]); 
	return 0;
}


 

/*
算法訓練 傳紙條

問題描述
  小淵和小軒是好朋友也是同班同學,他們在一起總有談不完的話題。一次素質拓展活動中,班上同學安排做成一個m行n列的矩陣,而小淵和小軒被安排在矩陣對角線的兩端,因此,他們就無法直接交談了。幸運的是,他們可以通過傳紙條來進行交流。紙條要經由許多同學傳到對方手裏,小淵坐在矩陣的左上角,座標(1,1),小軒坐在矩陣的右下角,座標(m,n)。從小淵傳到小軒的紙條只可以向下或者向右傳遞,從小軒傳給小淵的紙條只可以向上或者向左傳遞。
  在活動進行中,小淵希望給小軒傳遞一張紙條,同時希望小軒給他回覆。班裏每個同學都可以幫他們傳遞,但只會幫他們一次,也就是說如果此人在小淵遞給小軒紙條的時候幫忙,那麼在小軒遞給小淵的時候就不會再幫忙。反之亦然。
  還有一件事情需要注意,全班每個同學願意幫忙的好感度有高有低(注意:小淵和小軒的好心程度沒有定義,輸入時用0表示),可以用一個0-100的自然數來表示,數越大表示越好心。小淵和小軒希望儘可能找好心程度高的同學來幫忙傳紙條,即找到來回兩條傳遞路徑,使得這兩條路徑上同學的好心程度只和最大。現在,請你幫助小淵和小軒找到這樣的兩條路徑。
輸入格式
  輸入第一行有2個用空格隔開的整數m和n,表示班裏有m行n列(1<=m,n<=50)。
  接下來的m行是一個m*n的矩陣,矩陣中第i行j列的整數表示坐在第i行j列的學生的好心程度。每行的n個整數之間用空格隔開。
輸出格式
  輸出一行,包含一個整數,表示來回兩條路上參與傳遞紙條的學生的好心程度之和的最大值。
樣例輸入
3 3
0 3 9
2 8 5
5 7 0
樣例輸出
34
數據規模和約定
  30%的數據滿足:1<=m,n<=10
  100%的數據滿足:1<=m,n<=50
*/
#include<stdio.h>    
int n,m;   
int i,j,k;   
int Map[51][51];   
int F[111][51][51];   
int Max(int a,int b,int c,int d)   
{   
    if(a>=b&&a>=c&&a>=d)   
        return a;   
    if(b>=a&&b>=c&&b>=d)   
        return b;   
    if(c>=a&&c>=b&&c>=d)   
        return c;   
    if(d>=a&&d>=b&&d>=c)   
        return d;   
}   
int main()   
{   
    scanf("%d%d",&n,&m);   
    for(i=1;i<=n;i++)   
        for(j=1;j<=m;j++)   
            scanf("%d",&Map[i][j]);   
    for(k=1;k<=n+m-2;k++)   
        for(i=1;i<=n;i++)   
            for(j=1;j<=n;j++)   
                if(i==n&&j==n&&k==n+m-2)   
                    F[k][i][j]=Max(F[k-1][i-1][j],F[k-1][i][j-1],F[k-1][i][j],F[k-1][i-1][j-1])+Map[i][k+2-i]+Map[j][k+2-j];   
                else  if(i!=j&&k+2-i>=1&&k+2-j>=1)   
                    F[k][i][j]=Max(F[k-1][i-1][j],F[k-1][i][j-1],F[k-1][i][j],F[k-1][i-1][j-1])+Map[i][k+2-i]+Map[j][k+2-j];   
    printf("%d",F[n+m-2][n][n]);   
    return 0;   
}  

 

 

/*
算法訓練 Hankson的趣味題

問題描述
  Hanks 博士是BT (Bio-Tech,生物技術) 領域的知名專家,他的兒子名叫Hankson。現 在,剛剛放學回家的Hankson 正在思考一個有趣的問題。 今天在課堂上,老師講解了如何求兩個正整數c1 和c2 的最大公約數和最小公倍數。現 在Hankson 認爲自己已經熟練地掌握了這些知識,他開始思考一個“求公約數”和“求公 倍數”之類問題的“逆問題”,這個問題是這樣的:已知正整數a0,a1,b0,b1,設某未知正整 數x 滿足: 1. x 和a0 的最大公約數是a1; 2. x 和b0 的最小公倍數是b1。 Hankson 的“逆問題”就是求出滿足條件的正整數x。但稍加思索之後,他發現這樣的 x 並不唯一,甚至可能不存在。因此他轉而開始考慮如何求解滿足條件的x 的個數。請你幫 助他編程求解這個問題。
輸入格式
  輸入第一行爲一個正整數n,表示有n 組輸入數據。

  接下來的n 行每 行一組輸入數據,爲四個正整數a0,a1,b0,b1,每兩個整數之間用一個空格隔開。輸入 數據保證a0 能被a1 整除,b1 能被b0 整除。
輸出格式
  輸出共n 行。每組輸入數據的輸出結果佔一行,爲一個整數。
  對於每組數據:若不存在這樣的 x,請輸出0; 若存在這樣的 x,請輸出滿足條件的x 的個數;
樣例輸入
2
41 1 96 288
95 1 37 1776
樣例輸出
6
2
樣例說明
  第一組輸入數據,x 可以是9、18、36、72、144、288,共有6 個。
  第二組輸入數據,x 可以是48、1776,共有2 個。
數據規模和約定
  對於 50%的數據,保證有1≤a0,a1,b0,b1≤10000 且n≤100。
  對於 100%的數據,保證有1≤a0,a1,b0,b1≤2,000,000,000 且n≤2000。
*/
#include <stdio.h>
int gcd(int x,int y)
{
	return y?gcd(y,x%y):x;
}
int main()
{
	int n,a0,a1,b0,b1,x,cnt;
	scanf("%d",&n);
	while(n--)
	{
		scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
		cnt=0;
		for(x=1;x*x<=b1;x++)
		{
			if(b1%x==0)//如果x確實是b1的約數 
			{
				if(x%a1==0)//如果x確實是a1的倍數
				{ 
					if(gcd(x,a0)==a1&&(x/gcd(x,b0)*b0==b1))//乘以b0放在後面,否則運算結果溢出,尤其是第2個輸入用例 
						cnt++;
				} 
				if((b1/x)%a1==0&&x*x!=b1)//如果b1/x確實是a1的倍數,並且x與b1/x不相等 
				{ 
					if(gcd(b1/x,a0)==a1&&(b1/x/gcd(b1/x,b0)*b0==b1))//乘以b0放在後面,否則運算結果溢出 
					cnt++;
				} 
			}
		}
		printf("%d\n",cnt); 
	}
	return 0;
}


 

/*
算法訓練 接水問題

問題描述
  學校裏有一個水房,水房裏一共裝有m 個龍頭可供同學們打開水,每個龍頭每秒鐘的 供水量相等,均爲1。 現在有n 名同學準備接水,他們的初始接水順序已經確定。將這些同學按接水順序從1 到n 編號,i 號同學的接水量爲wi。接水開始時,1 到m 號同學各佔一個水龍頭,並同時打 開水龍頭接水。當其中某名同學j 完成其接水量要求wj 後,下一名排隊等候接水的同學k 馬上接替j 同學的位置開始接水。這個換人的過程是瞬間完成的,且沒有任何水的浪費。即 j 同學第x 秒結束時完成接水,則k 同學第x+1 秒立刻開始接水。若當前接水人數n’不足m, 則只有n’個龍頭供水,其它m?n’個龍頭關閉。 現在給出n 名同學的接水量,按照上述接水規則,問所有同學都接完水需要多少秒。
輸入格式
  第1 行2 個整數n 和m,用一個空格隔開,分別表示接水人數和龍頭個數。 第2 行n 個整數w1、w2、……、wn,每兩個整數之間用一個空格隔開,wi 表示i 號同 學的接水量。
輸出格式
  輸出只有一行,1 個整數,表示接水所需的總時間。
樣例輸入
5 3
4 4 1 2 1
樣例輸出
4
樣例輸入
8 4
23 71 87 32 70 93 80 76
樣例輸出
163
輸入輸出樣例 1 說明
  第1 秒,3 人接水。第1 秒結束時,1、2、3 號同學每人的已接水量爲1,3 號同學接完
  水,4 號同學接替3 號同學開始接水。
  第2 秒,3 人接水。第2 秒結束時,1、2 號同學每人的已接水量爲2,4 號同學的已接
  水量爲1。
  第3 秒,3 人接水。第3 秒結束時,1、2 號同學每人的已接水量爲3,4 號同學的已接
  水量爲2。4 號同學接完水,5 號同學接替4 號同學開始接水。
  第4 秒,3 人接水。第4 秒結束時,1、2 號同學每人的已接水量爲4,5 號同學的已接
  水量爲1。1、2、5 號同學接完水,即所有人完成接水。
  總接水時間爲4 秒。
數據規模和約定
  1 ≤ n ≤ 10000,1 ≤m≤ 100 且m≤ n;
  1 ≤ wi ≤ 100。
*/
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */


int max(int *p,int n)
{
    int Max = p[0];
    int i;
	for(i=1;i<n;i++)
	{
		if(p[i]>Max)
			   Max = p[i];	
	}	
	return Max;
}
int main(int argc, char *argv[]) {
	
	int m,n;  //m個水龍頭 n個同學
	int i,j,second,min,num;
	int *p;
	
	scanf("%d%d",&n,&m);
	p = (int *)malloc(n*sizeof(int));
	if( NULL == p )
	{
		printf("no enough memory!\n");
		return 0;
	}
	
	for(i=0;i<n;i++)
	{
	    scanf("%d",&p[i]);    
	}
	
	if( n<=m)
	{		
		printf("%d",max(p,n));
	}
	
	if( n>m)
	{
	    second = 0;
	  
	    num = 0;
	    while(n>m)
	    {
	        min = p[0]; 
	        j = 0;
		    for(i=0;i<m;i++)
		    {  
		       
		       if(min>p[i])
		       {
		            min = p[i];			
		            j=i;
		       }
			}	
		    
		    for(i=0;i<m;i++)
		    {
		    	p[i] -= min;
		    }
		    second += min;
			 
		    p[j]=p[m+num];  //第m+num同學取代取完水的同學 
		    num++;
		    n--;//取水同學減少一個 
		    
		    if( n<=m)
	        {	       	
		     
		     printf("%d",max(p,n)+second);
		
	        }
	   }
	}
	
	return 0;
}


 

/*
算法訓練 數組排序去重

問題描述
  輸入10個整數組成的序列,要求對其進行升序排序,並去掉重複元素。
輸入格式
  10個整數。
輸出格式
  多行輸出,每行一個元素。
樣例輸入
2 2 3 3 1 1 5 5 5 5
樣例輸出
1
2
3
5
*/
#include <stdio.h>
#include <stdlib.h>

int A[10];

int com(const void *a, const void *b)
{
	return *(int*)a-*(int*)b;
}

int main()
{
	int i;
	int last;
	for(i=0;i<10;i++)
	    scanf("%d",&A[i]);
	qsort(A,10,4,com);
	for(i=0;i<10;i++)
	    if(i)
	    {
	    	if(last!=A[i])
	    	{
	    		printf("%d\n",A[i]);
	            last=A[i];
	    	}
	    }
	    else
	    {
	        printf("%d\n",A[i]);
	    	last=A[i];
	    }
	return 0;
}

 

 

/*
算法訓練 會議中心

會議中心  Siruseri政府建造了一座新的會議中心。許多公司對租借會議中心的會堂很感興趣,他們希望能夠在裏面舉行會議。
  對於一個客戶而言,僅當在開會時能夠獨自佔用整個會堂,他纔會租借會堂。會議中心的銷售主管認爲:最好的策略應該是將會堂租借給儘可能多的客戶。顯然,有可能存在不止一種滿足要求的策略。
  例如下面的例子。總共有4個公司。他們對租借會堂發出了請求,並提出了他們所需佔用會堂的起止日期(如下表所示)。


開始日期
結束日期
公司1
4
9
公司2
9
11
公司3
13
19
公司4
10
17


  上例中,最多將會堂租借給兩家公司。租借策略分別是租給公司1和公司3,或是公司2和公司3,也可以是公司1和公司4。注意會議中心一天最多租借給一個公司,所以公司1和公司2不能同時租借會議中心,因爲他們在第九天重合了。
  銷售主管爲了公平起見,決定按照如下的程序來確定選擇何種租借策略:首先,將租借給客戶數量最多的策略作爲候選,將所有的公司按照他們發出請求的順序編號。對於候選策略,將策略中的每家公司的編號按升序排列。最後,選出其中字典序最小[1]的候選策略作爲最終的策略。
  例中,會堂最終將被租借給公司1和公司3:3個候選策略是{(1,3),(2,3),(1,4)}。而在字典序中(1,3)<(1,4)<(2,3)。
  你的任務是幫助銷售主管確定應該將會堂租借給哪些公司。
輸入格式
  輸入的第一行有一個整數N,表示發出租借會堂申請的公司的個數。第2到第N+1行每行有2個整數。第i+1行的整數表示第i家公司申請租借的起始和終止日期。對於每個公司的申請,起始日期爲不小於1的整數,終止日期爲不大於109的整數。
輸出格式
  輸出的第一行應有一個整數M,表示最多可以租借給多少家公司。第二行應列出M個數,表示最終將會堂租借給哪些公司。
數據規模和約定
  對於50%的輸入,N≤3000。在所有輸入中,N≤200000。
樣例輸入
4
4 9
9 11
13 19
10 17
樣例輸出
2
1 3

[1] 字典序指在字典中排列的順序,如果序列l1是序列l2的前綴,或者對於l1和l2的第一個不同位置j,l1[j]<l2[j],則l1比l2小。
*/
//C++
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <set>

const char fi[] = "convention.in";
const char fo[] = "convention.out";
const int maxN = 200010;
const int MAX = 0x3f3f3f3f,MIN = ~MAX;

struct Seg
{
	int L,R;
	Seg()
	{
	}
	Seg(int L,int R): L(L),R(R)
	{
	}
	bool operator<(const Seg &b) const
	{
		return L < b.L || L == b.L && R < b.R;
	}
};
std::set <Seg> S;
std::set <Seg>::iterator iter;
Seg req[maxN],seg[maxN],tmp[maxN];
int tab[maxN << 1],next[20][maxN << 1];
int n,cnt,Lim = 1,logLim;

void init_file()
{
	return;
}

inline int getint()
{
	int res = 0; char tmp;
	while(!isdigit(tmp = getchar()));
	do res = (res << 3) + (res << 1) + tmp - '0';
	while(isdigit(tmp = getchar()));
	return res;
}

void readdata()
{
	n = getint();
	for(int i = 0; i < n; ++i)
	{
		int L = getint(),R = getint();
		req[i] = Seg(L,R);
		tab[i << 1] = L;
		tab[(i << 1) + 1] = R;
	}
	return;
}

int plc(int x)
{
	for(int L = 0,R = Lim - 1; L < R + 1;)
	{
		int Mid = (L + R) >> 1;
		if(x == tab[Mid]) return Mid + 1;
		if(x < tab[Mid]) R = Mid - 1;
		else L = Mid + 1;
	}
}

bool cmp(const Seg &a,const Seg &b)
{
	return a.R < b.R || a.R == b.R && a.L > b.L;
}

void discrete()
{
	std::sort(tab,tab + (n << 1));
	for(int i = 1; i < n << 1; ++i)
		if(tab[i] != tab[i - 1])
			tab[Lim++] = tab[i];
	for(int i = 0; i < n; ++i)
		tmp[i] = req[i] = Seg(plc(req[i].L),
		plc(req[i].R));
	std::sort(tmp,tmp + n,cmp);
	//這裏必須要用一個臨時數組,
	//保證左界右界同時單調遞增。
	int p = 0; seg[cnt++] = tmp[0];
	for(int i = 1; i < n; ++i)
		if(tmp[i].L > tmp[p].L)
			seg[cnt++] = tmp[p = i];
	return;
}

void next_set()
{
	int p = cnt; next[0][Lim + 1] = MAX;
	for(int j = Lim; j; --j)
		if(p > -1 && j == seg[p - 1].L)
			next[0][j] = seg[--p].R + 1;
		else next[0][j] = next[0][j + 1];
		for(int i = 0;; ++i)
		{
			bool flag = 0;
			next[i + 1][Lim + 1] = MAX;
			for(int k = 1; k < Lim + 1; ++k)
			{
				if(next[i][k] == MAX)
					next[i + 1][k] = MAX;
				else next[i + 1][k] = next[i][next[i][k]];
				if(next[i + 1][k] < MAX) flag = 1;
			}
			if(!flag)
			{
				logLim = i; break;
			}
		}
		return;
}

int max_time(int L,int R)
{
	if(L > R++) return 0;
	int ans = 0,p = L;
	for(int i = logLim; i > -1 && p < R; --i)
		if(next[i][p] <= R)
		{
			p = next[i][p]; ans += 1 << i;
		}
	return ans;
}

bool query(int i)
{
	int L = req[i].L,R = req[i].R;
	iter = S.lower_bound(Seg(L,MAX));
	if(iter-- == S.begin()) return 0;
	if(iter->L > L || iter->R < R)
		return 0;
	int L1 = iter->L,R1 = iter->R;
	if(max_time(L1,L - 1)
		+ max_time(R + 1,R1)
		+ 1 < max_time(L1,R1))
		//這裏要滿足放進去過後不影響總的答案。
		return 0;
	S.erase(iter);
	if(L1 < L) S.insert(Seg(L1,L - 1));
	if(R < R1) S.insert(Seg(R + 1,R1));
	return 1;
}

void work()
{
	printf("%d\n",max_time(1,Lim));
	S.insert(Seg(1,Lim));
	for(int i = 0; i < n; ++i)
		if(query(i))
			printf("%d ",i + 1);
	printf("\n");
	return;
}

int main()
{
	init_file();
	readdata();
	discrete();
	next_set();
	work();
	return 0;
}

 

GoToTheNextPart

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章