統計數字:
題目大意:給你n個數,求每個數出現的次數。
題解:排個序,記錄每個數出現的個數,如果碰到不同的數了輸出並清零即可。時間複雜度:O(n log n),空間複雜度:O(n)。
#include<cstdio>
#include<algorithm>
using namespace std;
int n,i,a[200010],x,cnt;
int main(){
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
sort(a+1,a+1+n);
printf("%d",a[1]),cnt=1;
for(i=2;i<=n;i++)
if(a[i]==a[i-1])cnt++;
else printf(" %d\n%d",cnt,a[i]),cnt=1;
printf(" %d",cnt);
return 0;
}
字符串的展開:
題目大意:
給你一個字符串,只有小寫字母,數字和’-’,給定三個參數,當出現’-’時,s1,s2,s3按以下規則展開:
1.在輸入的字符串中,出現了減號’-’,減號兩側
同爲小寫字母或同爲數字,且按照ASCII 碼的順序,減號右邊的字符嚴格大於左邊的字符。
2.參數p1:展開方式。p1=1 時,對於字母子串,填充小寫字母;p1=2 時,對於字母子串,
填充大寫字母。這兩種情況下數字子串的填充方式相同。p1=3 時,不論是字母子串還是數字子串,都用與要填充的字母個數相同的星號’*’來填充。
3.參數p2:填充字符的重複個數。p2=k 表示同一個字符要連續填充k 個。例如,當p2=3
時,子串“d-h”應擴展爲”deeefffgggh”。減號兩側的字符不變。
4.參數p3:是否改爲逆序:p3=1 表示維持原有順序,p3=2 表示採用逆序輸出,注意這時
仍然不包括減號兩端的字符。例如當p1=1、p2=2、p3=2 時,子串”d-h”應擴展爲”dggffeeh”。
5.如果減號右邊的字符恰好是左邊字符的後繼,只刪除中間的減號,例如:”d-e”應輸出
爲”de”,”3-4”應輸出爲”34”。如果減號右邊的字符按照ASCII碼的順序小於或等於左邊字符,輸出時,要保留中間的減號,例如:”d-d”應輸出爲”d-d”,”3-1”應輸出爲”3-1”。len<=100.
題解:
直接模擬,注意判斷幾種特殊情況:’-’兩邊有一邊爲’-’,’-’在首尾的位置。時間複雜度:O(len*p2),空間複雜度:O(len)。
#include<cstdio>
#include<cstring>
int i,n,p1,p2,p3,j,k;
char s[110];
void putsz(){
if(p1==3)
for(j=s[i-1]+1;j<s[i+1];j++)
for(k=1;k<=p2;k++)putchar('*');
else
if(p3==1)
for(j=s[i-1]+1;j<s[i+1];j++)
for(k=1;k<=p2;k++)putchar(j);
else
for(j=s[i+1]-1;j>s[i-1];j--)
for(k=1;k<=p2;k++)putchar(j);
}
void putch(){
if(p1==3)
for(j=s[i-1]+1;j<s[i+1];j++)
for(k=1;k<=p2;k++)putchar('*');
else
if(p3==1){
if(p1==1)
for(j=s[i-1]+1;j<s[i+1];j++)
for(k=1;k<=p2;k++)putchar(j);
else
for(j=s[i-1]+1;j<s[i+1];j++)
for(k=1;k<=p2;k++)putchar(j-'a'+'A');
}
else{
if(p1==1)
for(j=s[i+1]-1;j>s[i-1];j--)
for(k=1;k<=p2;k++)putchar(j);
else
for(j=s[i+1]-1;j>s[i-1];j--)
for(k=1;k<=p2;k++)putchar(j-'a'+'A');
}
}
int main(){
scanf("%d%d%d",&p1,&p2,&p3);
scanf("%s",s+1);
n=strlen(s+1);
for(i=1;i<=n;i++)
if(s[i]!='-')putchar(s[i]);
else
if(s[i+1]<=s[i-1])putchar('-');
else
if((s[i-1]>='a'&&s[i-1]<='z'&&(s[i+1]<'a'||s[i+1]>'z'))||((s[i+1]<'0'||s[i+1]>'9')&&s[i-1]>='0'&&s[i-1]<='9')||i==1||i==n||s[i-1]=='-'||s[i+1]=='-')putchar('-');
else{
if(s[i-1]>='a'&&s[i-1]<='z')putch();
else putsz();
}
return 0;
}
矩陣取數遊戲:
題目大意:給定一個n*m的矩陣,取m次,每次在每一行取一個數,只能在每一行的首尾位置取,第i次的得分爲取的n個數的總和*2^i。求m次取數後的最大得分值。n,m<=80,aij<=1000.
題解:
unsigned long long的最大值爲2^64-1,所以顯然這題不管什麼算法都得套一個高精度……
由秦九韶公式可知:我們從內往外取數的話就每次對於當前ans*2即可。而從內往外取數的話有一個好處:每個數都有可能爲最後一個取的,然後接下來由這個數往左右擴展一個就得到了最後兩次取的數,一直擴展到左右兩邊即可。
這樣子就變成了一個經典的區間DP了。以長度爲第一重循環,對於長度爲len-1的最優取法我們都已經得到了,而對於一段要取的序列l,r,要麼就是先取l,r-1,然後最後取r,或者先取l+1,r,最後取l,兩者取個max即可。
設f[i][j]代表從l開始長度爲j的數列的最大得分值,則:
f[i][j]=max(2*(f[i][j-1]+a[j]),2*(f[i+1][j-1]+a[i]))。時間複雜度:O(n*m*m*G),空間複雜度:O(m*m*G)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,i,j,k,ans[210],f[90][90][110],a[90],na[2][110];
void plus(int a[],int b[]){
a[0]=max(a[0],b[0]);
for(int i=1;i<=a[0];i++){
a[i]+=b[i];
a[i+1]+=a[i]/10;
a[i]%=10;
}
if(a[a[0]+1])a[0]++;
while(a[a[0]]>=10){
a[a[0]+1]+=a[a[0]]/10;
a[a[0]]%=10;
a[0]++;
}
}
bool cmp(int a[],int b[]){
if(a[0]==b[0]){
for(int i=a[0];i;i--)
if(a[i]==b[i])continue;
else return a[i]<b[i];
}
return a[0]<b[0];
}
void mul(int a[]){
for(int i=1;i<=a[0];i++)a[i]*=2;
for(int i=1;i<=a[0];i++)a[i+1]+=a[i]/10,a[i]%=10;
if(a[a[0]+1])a[0]++;
while(a[a[0]]>=10){
a[a[0]+1]+=a[a[0]]/10;
a[a[0]]%=10;
a[0]++;
}
}
void pplus(int a[],int b){
int i=1;
a[i]+=b;
while(a[i]>=10){
a[i+1]+=a[i]/10;
a[i]%=10;
i++;
}
a[0]=max(a[0],i);
}
int main(){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
memset(f,0,sizeof(f));
for(j=1;j<=m;j++)scanf("%d",&a[j]),f[j][1][0]=1,f[j][1][1]=2*a[j];
for(j=2;j<=m;j++){
for(k=1;k<=m-j+1;k++){
memset(na,0,sizeof(na));
plus(na[0],f[k][j-1]);
pplus(na[0],a[k+j-1]);
mul(na[0]);
plus(na[1],f[k+1][j-1]);
pplus(na[1],a[k]);
mul(na[1]);
if(cmp(na[0],na[1]))memcpy(f[k][j],na[1],sizeof(na[1]));
else memcpy(f[k][j],na[0],sizeof(na[0]));
}
}
plus(ans,f[1][m]);
}
for(i=ans[0];i;i--)printf("%d",ans[i]);
return 0;
}
樹網的核:
題目大意:給定一棵樹,一段路徑記爲F,D(i,F),代表i到F上最近的點的距離,一段路徑的偏心距爲max(D(i,F))(i<=n)。求樹直徑上的一段長度<=x的路徑的偏心距的最小值。n<=300.
題解:
首先dfs一遍求出相鄰點兩兩之間的距離,再用floyd求出所有點對間距離,並求出所有直徑。我們需要知道這兩個定理:1.一條路徑上的偏心距必定是由某條直徑的端點到這條路徑上的距離。2.核一定是所有直徑的交集的一部分。因此我們可以任取一條直徑,對於該直徑上的一段路徑,判斷是否<=s,然後求出該直徑的端點與該路徑的距離,更新答案即可。時間複雜度:O(n^3),空間複雜度:O(n^2)。然而這題數據太水了,怎麼做都能過。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,s,i,j,k,f[310][310],x,y,z,ans=2147483647,que[310],w,maxn,maxi,maxj;
int main(){
scanf("%d%d",&n,&s);
memset(f,1,sizeof(f));
for(i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
f[i][i]=0;
f[x][y]=f[y][x]=z;
}
f[n][n]=0;
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(f[i][j]>f[i][k]+f[k][j]&&i!=j)f[i][j]=f[i][k]+f[k][j];
for(i=1;i<=n;i++)
for(j=i;j<=n;j++)
if(f[i][j]>maxn){
maxn=max(maxn,f[i][j]);
maxi=i,maxj=j;
}
for(i=1;i<=n;i++)
if(f[maxi][i]+f[i][maxj]==f[maxi][maxj])que[++w]=i;
for(i=1;i<=w;i++)
for(j=i;j<=w;j++)
if(f[que[i]][que[j]]<=s)ans=min(max(min(f[maxi][que[i]],f[maxi][que[j]]),min(f[maxj][que[i]],f[maxj][que[j]])),ans);
printf("%d",ans);
return 0;
}