給定一個序列,每次詢問序列中第l個數到第r個數中第K大的數是哪一個。node
第一行包含一個數n,表示序列長度。算法
第二行包含n個正整數,表示給定的序列。數組
第三個包含一個正整數m,表示詢問個數。學習
接下來m行,每行三個數l,r,K,表示詢問序列從左往右第l個數到第r個數中,從大往小第K大的數是哪一個。序列元素從1開始標號。測試
對於30%的數據,n,m<=100;spa
對於100%的數據,n,m<=1000;調試
保證k<=(r-l+1),序列中的數<=106。code
#include<stdio.h> #include<stdlib.h> #include<algorithm> using namespace std; int compare(int a,int b)//降序排序 { return a>b; } int main() { int n,m,i; scanf("%d",&n); int a[n+1],b[n+1]; for(i=1;i<=n;i++)scanf("%d",&a[i]); int l,r,k,j; scanf("%d",&m); while(m--) { scanf("%d%d%d",&l,&r,&k); for(i=l,j=1;i<=r;i++,j++)b[j]=a[i]; sort(b+1,b+(r-l+2),compare); // for(i=1;i<=(r-l)+1;i++)printf("%d ",b[i]); // printf("\n"); printf("%d\n",b[k]); } return 0; }
問題描述blog
已知一個正整數N,問從1~N-1中任選出三個數,他們的最小公倍數最大能夠爲多少。排序
輸入格式
輸入一個正整數N。
輸出格式
輸出一個整數,表示你找到的最小公倍數。
樣例輸入
9
樣例輸出
504
數據規模與約定
1 <= N <= 106。
題目分析
當n爲奇數時,答案必定是n*(n-1)*(n-2)。
當n爲偶數時,答案多是(n-1)*(n-2)*(n-3),也多是n*a*b,其中a>=n-3。
參考程序
#include<stdio.h> #include<stdlib.h> #include<time.h> #include<math.h> //展轉相除法求最大公約數,最小公倍數 long long RoudDiv(long long m,long long n) { long long m1,n1; if( m >=n) { m1 = m; n1 = n; } else { m1 = n; n1 = m; } long long r = m1%n1; while( r!=0 ) { m1 = n1; n1 = r; r = m1%n1; } //最大公約數n,求最小公倍數 return m*n/n1; } //求三個數的最小公倍數 long long getMulMin(int a,int b,int c) { return RoudDiv(RoudDiv(a,b),c); } //檢查min和max是否互素 int IsShus( int min,int max) { int i; if( min>max ) { int temp = max; max = min; min = temp; } for( i=2; i<=min;i++ ) { if( min%i==0 && max%i==0 ) { break; } } if ( i>min ) { return 1; } else { return 0; } } long long FindMaxMul(int* data) { int i,j,k; int resul[3]; int n = data[0]; if( n%2 != 0) { return getMulMin( data[n],data[n-1],data[n-2]); } long long max = getMulMin( data[n-1],data[n-2],data[n-3]); long long a; // printf("data = %d %d %d\n",data[n-1],data[n-2],data[n-3]); // printf("max=%I64d\n",max); resul[0] = data[n-1]; for( j=n-1; j>=n-3; j--) { for( k=j-1 ; k>=1; k--) { if( IsShus(data[k],data[n]) && IsShus(data[k],data[j]) ) { if ( (a = getMulMin( data[n],data[j],data[k]) )> max) { resul[0] = data[n]; resul[1] = data[j]; resul[2] = data[k]; max = a; // printf("data = %d %d %d\n",data[n],data[j],data[k]); } break; } } } // printf("%d %d %d\n",resul[0],resul[1],resul[2]); // printf("%d",a); return max; } int main() { int n,i; scanf("%d",&n); int data[n+1]; data[0] = n; for ( i=1; i<=n ; i++) { data[i] = i; } if ( n==1 || n==2) { printf("%d",n); } else { // printf("\n"); clock_t start, finish; start = clock(); long long a=FindMaxMul(data); printf("%I64d",a); finish = clock(); double duration = (double)(finish - start); // printf( "\n%f 毫秒\n", duration ); } return 0; }
《3》 K好數
問題描述
若是一個天然數N的K進製表示中任意的相鄰的兩位都不是相鄰的數字,那麼咱們就說這個數是K好數。求L位K進制數中K好數的數目。例如K = 4,L = 2的時候,全部K好數爲十一、1三、20、2二、30、3一、33 共7個。因爲這個數目很大,請你輸出它對1000000007取模後的值。
輸入格式
輸入包含兩個正整數,K和L。
輸出格式
輸出一個整數,表示答案對1000000007取模後的值。
樣例輸入
4 2
樣例輸出
7
數據規模與約定
對於30%的數據,KL <= 106;
對於50%的數據,K <= 16, L <= 10;
對於100%的數據,1 <= K,L <= 100。
題目分析
此題用動態規劃求解。求L位K進制數中K好數的數目,咱們能夠先求出L-1位K好數,L-2位K好數......1位K好數。設F[i][j]表示i位以數字j結尾的K好數數目,1<=i<=L,0<=j<K
而
既i位的以j結尾的K好數,等於i-1位的且最後一位與j不相鄰的K好數個數的和。
心得體會
在數字大於計算機能夠表示的範圍時,有可能迭代過程就是錯的了。而對於數據很大的時候,單步調試的方法彷佛是行不通的,因此這個是有就應該在適當的位置輸出迭代結果,觀察輸出是否與預想的一致,而後慢慢將範圍縮小,縮小到能夠進行單步調試的範圍。固然,若是發現了錯誤的話就不必進行單步調試了。
在這個算法中,對最後答案取餘,其實在迭代過程當中f[i][j]早已越界,因此應該在迭代過程當中就只存餘數。在調試過程當中,總以錯誤的答案爲參照去懷疑正確的答案,這也是一個敗筆。應該堅持調試的時候輸出迭代過程,這樣才能很好的判斷答案的正確與否。
(a+b) mod c = ((a mod c) + (b mod c)) mod c
數組最大能夠到data[100000]
參考代碼
#include<stdio.h> #include<stdlib.h> #include<math.h> #include<time.h> # define MOD 1000000007 long long f[101][100000]; //求K好數 long long KGoodNum(int l,int k) { int i,j,p; //以數字i結尾的1位K好數的數目 for( i=0; i<k; i++) { f[1][i] = 1; } for ( i=2; i<=l; i++ )//求2位開始到l位的K好數 { for( j=0; j<k; j++ )//i位的最後一位數j { f[i][j] = 0; for ( p=0; p<k; p++)//i-1位的最後一位數p { if( p+1!=j && p-1!=j ) { f[i][j] =(f[i][j] + f[i-1][p])%MOD;//只用存儲餘數便可 // printf("i= %d; j=%d; f[i][j]= %I64d\n",i,j,f[i][j]); } } } } //l位的K好數總數 long long sum = 0; for ( i=1; i<k; i++) { sum += f[l][i]; } return sum%MOD; } int main() { int k,l; clock_t start,finish; scanf("%d%d",&k,&l); start = clock(); long long sum = 0; sum = KGoodNum(l,k); printf("%I64d",sum); finish = clock(); double t = (double)(finish - start); // printf("\ntime = %f",t); return 0; }
《4》 結點選擇
問題描述
有一棵 n 個節點的樹,樹上每一個節點都有一個正整數權值。若是一個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?
輸入格式
第一行包含一個整數 n 。
接下來的一行包含 n 個正整數,第 i 個正整數表明點 i 的權值。
接下來一共 n-1 行,每行描述樹上的一條邊。
輸出格式
輸出一個整數,表明選出的點的權值和的最大值。
樣例輸入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
樣例輸出
12
樣例說明
選擇三、四、5號點,權值和爲 3+4+5 = 12 。
數據規模與約定
對於20%的數據, n <= 20。
對於50%的數據, n <= 1000。
對於100%的數據, n <= 100000。
權值均爲不超過1000的正整數。
題目分析
本題應該是用樹形動態規劃是比較合適的。其中設F[v][1]表示選擇節點i的最大權值,F[v][0]表示不選擇節點i的最大權值,pow[i]表示節點i的權值,再設與v相鄰的節點u爲v的孩子節點,則可有
F[v][1]=F[u][0]+pow[v],F[v][0]=max{F[u][0],F[u][1]};若v有多個孩子節點,u1,u2,....uk,可有:
根據公式進行樹形動歸就能夠將問題解決。可是這裏要注意的是,樹的存儲結構選擇至關重要,若是用二維數組存儲樹,那麼空間最大達到100000*100000,但是纔有100000-1條邊,因此對空間的浪費是客觀的。而此題涉及到有關孩子的操做比較多,綜合狀況,採用了孩子鏈表表示法存儲樹的結構。
參考代碼
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<algorithm> #define M 100100 using namespace std; typedef struct Node { int vex; Node* next; }Child; Child* head[M]; int f[M][2],pow[M],visit[M]; //添加邊 void addADJ(int u,int v) { Child *p,*q; p=(Child*)malloc(sizeof(Child)); p->vex=v; p->next=head[u]; head[u]=p; q=(Child*)malloc(sizeof(Child)); q->vex=u; q->next=head[v]; head[v]=q; } //動態規劃 void GetResul(int v) { visit[v]=1; Child *p; for(p=head[v];p!=NULL;p=p->next) { if(visit[p->vex]==0) { GetResul(p->vex); f[v][1] = f[v][1]+f[p->vex][0]; f[v][0]+=max(f[p->vex][0],f[p->vex][1]); } } f[v][1]+=pow[v]; } int main() { int i,j,u,v,n; memset(head,NULL,sizeof(head)); memset(f,0,sizeof(f)); memset(visit,0,sizeof(visit)); scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&pow[i]); } for(i=1;i<n;i++) { scanf("%d%d",&u,&v); addADJ(u,v); } GetResul(1); printf("%d\n",max(f[1][0],f[1][1])); return 0; }
問題描述
給定一個n個頂點,m條邊的有向圖(其中某些邊權可能爲負,但保證沒有負環)。請你計算從1號點到其餘點的最短路(頂點從1到n編號)。
輸入格式
第一行兩個整數n, m。
接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度爲l的邊。
輸出格式
共n-1行,第i行表示1號點到i+1號點的最短路。
樣例輸入
3 3
1 2 -1
2 3 -1
3 1 2
樣例輸出
-1
-2
數據規模與約定
對於10%的數據,n = 2,m = 2。
對於30%的數據,n <= 5,m <= 10。
對於100%的數據,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其餘全部頂點。
題目分析
起初剛看這道題目的時候,第一感受就想到了鄰接矩陣加Dijkstra求最短路徑。可是程序提交後,竟然是錯誤的,結果發現內存佔用率很大。再回頭看題目,發現這個圖相對是比較稀疏的圖,
因此用鄰接數組存儲會浪費不少空間。在這樣的狀況下,應該選擇鄰接表存儲圖是比較合適的。通過改進後,提交代碼經過。因此再看到題目後應該仔細分析題目的數據規模,選擇合適的存儲結構,直接影響到截正確的解答問題。
參考代碼
/* 測試已經過 用鄰接表實現 */ #include<stdio.h> #include<algorithm> #include<queue> #include<string.h> using namespace std; const int maxsize=200000; const int inf = 1000000; typedef struct XNode { int pow; int adjvex; struct XNode* next; }Node; Node* head[maxsize]; int visit[maxsize],dist[maxsize]; //添加邊 void AddAdj(int u,int v,int l) { Node *p; p = (Node*)malloc(sizeof(Node)); p->pow=l; p->adjvex=v; p->next=head[u]; head[u]=p; } //求最短路徑 void shortpath(int n) { int i,j,u,v,w; queue<int> Q; Node *p; for(i=1;i<=n;i++) { visit[i]=0; dist[i]=inf; } //第一個節點入隊列 Q.push(1); dist[1]=0; visit[1]=1; while(!Q.empty())//檢查全部節點 { u=Q.front(); Q.pop(); for(p=head[u];p!=NULL;p=p->next) { v=p->adjvex; w=p->pow; if(dist[u]+w<dist[v])//經過u到v會比原路徑到達v要近 { dist[v]=dist[u]+w; if(!visit[v]) { visit[v]=1; Q.push(v); } } } } } int main() { int n,m,u,v,w; scanf("%d%d",&n,&m); int i; memset(head,NULL,sizeof(head)); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); AddAdj(u,v,w); } shortpath(n); for(i=2;i<=n;i++) printf("%d\n",dist[i]); return 0; }
問題描述
Farmer John變得很是懶,他不想再繼續維護供奶牛之間供通行的道路。道路被用來鏈接N個牧場,牧場被連續地編號爲1到N。每個牧場都是一個奶牛的家。FJ計劃除去P條道路中儘量多的道路,可是還要保持牧場之間 的連通性。你首先要決定那些道路是須要保留的N-1條道路。第j條雙向道路鏈接了牧場Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),並且走完它須要Lj的時間。沒有兩個牧場是被一條以上的道路所鏈接。奶牛們很是傷心,由於她們的交通系統被削減了。你須要到每個奶牛的住處去安慰她們。每次你到達第i個牧場的時候(即便你已經到過),你必須花去Ci的時間和奶牛交談。你每一個晚上都會在同一個牧場(這是供你選擇的)過夜,直到奶牛們都從悲傷中緩過神來。在早上 起來和晚上回去睡覺的時候,你都須要和在你睡覺的牧場的奶牛交談一次。這樣你才能完成你的 交談任務。假設Farmer John採納了你的建議,請計算出使全部奶牛都被安慰的最少時間。
輸入格式
第1行包含兩個整數N和P。
接下來N行,每行包含一個整數Ci。
接下來P行,每行包含三個整數Sj, Ej和Lj。
輸出格式
輸出一個整數, 所須要的總時間(包含和在你所在的牧場的奶牛的兩次談話時間)。
樣例輸入
5 6
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
樣例輸出
178
數據規模與約定
5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。
題目分析
由數據規模能夠看出,圖的邊相對比較稀疏,因此用Kruskal算法求最小生成樹。既,每次從剩餘的邊中選擇一條權值最短的邊,而且加邊之後不會構成環,直到全部的頂點都連通爲止。由題可知道,源點既爲C值最小的點,而每條道路都會走兩次,因此道路權值應該爲2*L+Cs+Ce;
參看代碼
#include<stdio.h> #include<stdlib.h> #include<algorithm> #define M 100000 using namespace std; //邊節點 typedef struct node { int s,e,l; }Edeg; Edeg edg[M]; int c[M],pre[M]; int compare(Edeg e1,Edeg e2)//升序 { return e1.l<e2.l; } int findRoot(int n)//查找同集合的表明元素 { if(pre[n]==n)return n; int t= findRoot(pre[n]); pre[n]=t; return t; } int main() { // FILE *f; // f=fopen("安慰奶牛data.txt","r+"); int i,j,k,n,p,minn=M; // fscanf(f,"%d%d",&n,&p); scanf("%d%d",&n,&p); //輸入節點權值 for(i=1;i<=n;i++) { // fscanf(f,"%d",&c[i]); scanf("%d",&c[i]); if(c[i]<minn)minn=c[i]; pre[i]=i; } //輸入邊 for(i=1;i<=p;i++) { //fscanf(f,"%d%d%d",&edg[i].s,&edg[i].e,&edg[i].l); scanf("%d%d%d",&edg[i].s,&edg[i].e,&edg[i].l); edg[i].l =2*edg[i].l+c[edg[i].s]+c[edg[i].e];//邊的權值 } int r1,r2,sum=0; sort(edg+1,edg+p+1,compare); for(i=1;i<=p;i++) { r1=findRoot(edg[i].s); r2=findRoot(edg[i].e); if(r1!=r2) { sum+=edg[i].l; pre[r2]=r1; } } sum+=minn;//晚上回來還要跟源點奶牛進行一次交談 printf("%d\n",sum); return 0; }
問題描述
Alice是一個讓人很是愉躍的人!他老是去學習一些他不懂的問題,而後再想出許多稀奇古怪的題目。這幾天,Alice又沉浸在逆序對的快樂當中,他已近學會了如何求逆序對對數,動態維護逆序對對數等等題目,他認爲把這些題讓你作簡直是太沒追求了,因而,通過一天的思考和完善,Alice終於拿出了一道他認爲差很少的題目:
有一顆2n-1個節點的二叉樹,它有剛好n個葉子節點,每一個節點上寫了一個整數。若是將這棵樹的全部葉子節點上的數從左到右寫下來,便獲得一個序列a[1]…a[n]。如今想讓這個序列中的逆序對數量最少,但惟一的操做就是選樹上一個非葉子節點,將它的左右兩顆子樹交換。他能夠作任意屢次這個操做。求在最優方案下,該序列的逆序對數最少有多少。
Alice本身已近想出了題目的正解,他打算拿來和你分享,他要求你在最短的時間內完成。
輸入格式
第一行一個整數n。
下面每行,一個數x。
若是x=0,表示這個節點非葉子節點,遞歸地向下讀入其左孩子和右孩子的信息,若是x≠0,表示這個節點是葉子節點,權值爲x。
輸出格式
輸出一個整數,表示最少有多少逆序對。
樣例輸入
3
0
0
3
1
2
樣例輸出
1
數據規模與約定
對於20%的數據,n <= 5000。
對於100%的數據,1 <= n <= 200000,0 <= a[i]<2^31。
問題描述
有n個格子,從左到右放成一排,編號爲1-n。
共有m次操做,有3種操做類型:
1.修改一個格子的權值,
2.求連續一段格子權值和,
3.求連續一段格子的最大值。
對於每一個二、3操做輸出你所求出的結果。
輸入格式
第一行2個整數n,m。
接下來一行n個整數表示n個格子的初始權值。
接下來m行,每行3個整數p,x,y,p表示操做類型,p=1時表示修改格子x的權值爲y,p=2時表示求區間[x,y]內格子權值和,p=3時表示求區間[x,y]內格子最大的權值。
輸出格式
有若干行,行數等於p=2或3的操做總數。
每行1個整數,對應了每一個p=2或3操做的結果。
樣例輸入
4 3
1 2 3 4
2 1 3
1 4 3
3 1 4
樣例輸出
6
3
數據規模與約定
對於20%的數據n <= 100,m <= 200。
對於50%的數據n <= 5000,m <= 5000。
對於100%的數據1 <= n <= 100000,m <= 100000,0 <= 格子權值 <= 10000。
題目分析
我第一次是用數組存樹的信息,結果沒經過,可是也沒發現是什麼問題。後來就採用二叉鏈表來存儲線段樹,結果經過了。雖然沒有發現第一個緣由出錯在哪,不過用鏈表動態存儲樹的話仍是相對比較方便的,用遞歸的思想對樹進行操做也是很方便快捷的。
參考代碼
#include<stdio.h> #include<stdlib.h> #include<algorithm> using namespace std; typedef struct node { int sum,max,a,b; struct node *left,*right; }TNode; TNode* Buld(int a,int b,TNode* r) { r=(TNode*)malloc(sizeof(TNode)); r->a=a;r->b=b; r->sum=0;r->max=0; if(a==b) { r->left=r->right=NULL; } else { r->left=Buld(a,(a+b)/2,r->left); r->right=Buld((a+b)/2+1,b,r->right); } return r; } void Insert(TNode *T,int x,int y) { if(T->a==x&&T->b==x) { T->sum=y; T->max=y; } else { if(x<=(T->a+T->b)/2) { Insert(T->left,x,y); } else { Insert(T->right,x,y); } T->sum=T->left->sum+T->right->sum; T->max=T->left->max>T->right->max?T->left->max:T->right->max; } } int GetSum(TNode *T,int x,int y) { int sum=0; if(x<=T->a&&T->b<=y) sum+=T->sum; else { int mid=(T->a+T->b)/2; if(y<=mid) sum+=GetSum(T->left,x,y); else if(x>mid) sum+=GetSum(T->right,x,y); else sum +=(GetSum(T->left,x,y)+GetSum(T->right,x,y)); } return sum; } int GetMax(TNode *T,int x,int y) { int max; if(x<=T->a&&T->b<=y) max=T->max; else { int mid = (T->a+T->b)/2; if(y<=mid) max=GetMax(T->left,x,y); else if(x>mid) max=GetMax(T->right,x,y); else { int a1=GetMax(T->left,x,y); int a2=GetMax(T->right,x,y); max=a1>a2?a1:a2; } } return max; } void display(TNode *p) { if(p==NULL)return; printf("a=%d b=%d sum=%d max=%d\n",p->a,p->b,p->sum,p->max); display(p->left); display(p->right); } int main() { TNode *tree=NULL; int i,n,m,p,x,y,a,sum,max; scanf("%d%d",&n,&m); tree=Buld(1,n,tree); for(i=1;i<=n;i++) { scanf("%d",&a); Insert(tree,i,a); } while(m--) { // display(tree); scanf("%d%d%d",&p,&x,&y); if(p==1) Insert(tree,x,y); else if(p==2) { sum=GetSum(tree,x,y); printf("%d\n",sum); } else if(p==3) { max=GetMax(tree,x,y); printf("%d\n",max); } } return 0; }