1. 程式人生 > 實用技巧 >大假期集訓模擬賽10

大假期集訓模擬賽10

字串的距離

題目描述

設有字串 \(X\),我們稱在 \(X\) 的頭尾及中間插入任意多個空格後構成的新字串為 \(X\) 的擴充套件串,如字串 \(X\) 為 “ \(abcbcd\) ”,則字串 “ \(abcb \square cd\) ”,” \(\square a \square bcbcd \square\) ” 和 “\(abcb \square cd \square\) ” 都是 \(X\) 的擴充套件串,這裡“ \(\square\) ”代表空格字元。

如果 \(A_1\) 是字串 \(A\) 的擴充套件串,\(B_1\) 是字串 \(B\) 的擴充套件串,\(A_1\)\(B_1\)

具有相同的長度,那麼我們定義字串 \(A_1\)\(B_1\) 的距離為相應位置上的字元的距離總和:

  • 兩個非空格字元的距離定義為它們的 \(ASCII\) 碼的差的絕對值,
  • 空格字元其它任意字元之間的距離為已知的定值 \(K\)
  • 空格字元空格字元的距離為 \(0\)

在字串 \(A,B\) 的所有擴充套件串中,必定存在兩個等長的擴充套件串 \(A_1,B_1\),使得 \(A_1\)\(B_1\) 之間的距離達到最小,我們將這一距離定義為字串 \(A,B\) 的距離。

請你寫一個程式,求出字串 \(A,B\) 的距離。

輸入格式

輸入檔案第一行為字串 \(A\)

第二行為字串 \(B\)

\(A,B\) 均由小寫字母組成且長度均不超過 \(2000\)

第三行為一個整數 \(K,1\leq K\leq 100\),表示空格與其它字元的距離。

輸出格式

輸出檔案僅一行包含一個整數,表示要求的字串 \(A,B\) 的距離。

樣例

樣例輸入

cmc
snmn
2

樣例輸出

10

思路

很裸的一個 \(dp\),一開始打了一手 \(DFS\),找了找思路,跑了一遍差不多長度為 \(10\) 的字串,直接 \(TLE\),本地都跑不出來,於是切換了一下思路,改用 \(n^2\)\(dp\),定義 \(f[i][j]\) 為第一個字串用了 \(i\) 個字元,第二個字串用了 \(j\) 個字元(不算空格),從上面的狀態轉移過來即可。

本來分四種情況:

  • 第一個字串選用字元,第二個字串選用空格
  • 第一個字串選用空格,第二個字串選用字元
  • 第一個字串選用字元,第二個字串選用字元
  • 第一個字串選用空格,第二個字串選用空格(很明顯不用考慮,不然會直接跑死)。

程式碼

#include <bits/stdc++.h>
using namespace std;

const int maxn=2e3+50,INF=0x3f3f3f3f;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

char a[maxn],b[maxn];
int n,m,k;
int f[maxn][maxn];

int main(){
	memset(f,0x3f,sizeof(f));
	scanf("%s%s",a+1,b+1);
	k=read();
	n=strlen(a+1);
	m=strlen(b+1);
	f[0][0]=0;
	f[1][1]=min(2*k,abs(a[1]-b[1]));
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++){//注意陣列不要越界,對拍的時候發現的問題,不然會出現負數
			if(i>=1) f[i][j]=min(f[i-1][j]+k,f[i][j]);
			if(j>=1) f[i][j]=min(f[i][j-1]+k,f[i][j]);
			if(i>=1&&j>=1) f[i][j]=min(f[i-1][j-1]+abs(a[i]-b[j]),f[i][j]);
		}
	}
	printf("%d\n",f[n][m]);
	return 0;
}

Blue Mary的戰役地圖

洛谷P4398

題目描述

\(Blue Mary\) 最近迷上了玩 \(Starcraft\) (星際爭霸) 的 \(RPG\) 遊戲。她正在設法尋找更多的戰役地圖以進一步提高自己的水平。

由於 \(Blue Mary\) 的技術已經達到了一定的高度,因此,對於用同一種打法能夠通過的戰役地圖,她只需要玩一張,她就能瞭解這一類戰役的打法,然後她就沒有興趣再玩兒這一類地圖了。而網上流傳的地圖有很多都是屬於同一種打法,因此 \(Blue Mary\) 需要你寫一個程式,來幫助她判斷哪些地圖是屬於同一類的。

具體來說, \(Blue Mary\) 已經將戰役地圖編碼為 \(n\times n\)的矩陣,矩陣的每個格子裡面是一個 \(32\) 位(有符號)正整數。對於兩個矩陣,他們的相似程度定義為他們的最大公共正方形矩陣的邊長。兩個矩陣的相似程度越大,這兩張戰役地圖就越有可能是屬於同一類的。

輸入格式

輸入檔案的第一行包含一個正整數 \(n\)

以下 \(n\) 行,每行包含 \(n\) 個正整數,表示第一張戰役地圖的代表矩陣。

再以下 \(n\) 行,每行包含 \(n\) 個正整數,表示第二張戰役地圖的代表矩陣。

輸出格式

輸出檔案僅包含一行。這一行僅有一個正整數,表示這兩個矩陣的相似程度。

樣例

樣例輸入

3
1 2 3
4 5 6
7 8 9
5 6 7
8 9 1
2 3 4

樣例輸出

2

資料範圍與提示

子矩陣:

5 6
8 9

為兩個地圖的最大公共矩陣

對於 \(100\%\) 的資料,\(1\leq n\leq 100\)

思路

考試的時候打了一手 \(n^5\) 的暴力,好像思路有點問題,直接 \(W\) 掉了,資料比較水,用一手 \(n^4\)\(dp\) 也能過。

定義 \(f[i][j][l][r]\) 的陣列,表示第一個矩陣到右下角 \((i,j)\),第二個矩陣到右下角 \((l,r)\),所能得到的最大相似程度,從這個位置的左邊、右邊、左上轉移過來,取最小值。

小聲bb:當時交的時候,加了一堆 \(register\),反而負優化了,建議 \(for\) 迴圈多的時候,不要用。

正解是比較裸的二維 \(hash\) 板子,多打幾遍就好了,儘管我還沒記住

程式碼

dp

#include <bits/stdc++.h>
using namespace std;

const int maxn=100+50,INF=0x3f3f3f3f;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n;
int ans;
int a[maxn][maxn],b[maxn][maxn];
int f[maxn][maxn][maxn][maxn];

int main(){
	n=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=read();
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=read();
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int l=1;l<=n;l++){
				for(int r=1;r<=n;r++){
					if(a[i][j]==b[l][r]){
						f[i][j][l][r]=min(f[i-1][j-1][l-1][r-1],min(f[i][j-1][l][r-1],f[i-1][j][l-1][r]))+1;
						ans=max(ans,f[i][j][l][r]);						
					}
				}
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

hash

#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;

const int maxn=100+50,INF=0x3f3f3f3f;
const int RowBase=11,ColBase=13;//行與列的進位制
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
int n;
int Row[maxn],Col[maxn];
int a[maxn][maxn],b[maxn][maxn];

int Calc(int a[][maxn],int x,int y,int h){//二維hash的標準式
	return a[x][y]-a[x-h][y]*Col[h]-a[x][y-h]*Row[h]+a[x-h][y-h]*Row[h]*Col[h];
}

void Solve(){
	for(int i=1;i<=n;i++){//每一行的hash值
		for(int j=1;j<=n;j++){
			a[i][j]=a[i][j-1]*RowBase+a[i][j];
		}
	}
	for(int i=1;i<=n;i++){//每一列的hash值
		for(int j=1;j<=n;j++){
			a[i][j]=a[i-1][j]*ColBase+a[i][j];
		}
	}
	map<int,bool> mp;
	for(int d=0;d<=n;d++){
		for(int i=d;i<=n;i++){
			for(int j=d;j<=n;j++){
				mp[Calc(a,i,j,d)]=1;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=b[i][j-1]*RowBase+b[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=b[i-1][j]*ColBase+b[i][j];
		}
	}
	for(int d=n;d>=0;d--){
		for(int i=d;i<=n;i++){
			for(int j=d;j<=n;j++){
				if(mp.find(Calc(b,i,j,d))!=mp.end()){
					printf("%lld\n",d);
					return;
				}
			}
		}
	}
} 

signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=read();
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=read();
		}
	}
	Row[0]=Col[0]=1;//基數
	for(int i=1;i<=n;i++){//行的基數
		Row[i]=Row[i-1]*RowBase;
	}
	for(int i=1;i<=n;i++){//列的基數
		Col[i]=Col[i-1]*ColBase;
	}
	Solve();
	return 0;
}

反質數

洛谷P1463

題目描述

對於任何正整數 \(x\),其約數的個數記作 \(g(x)\)。例如 \(g(1)=1,g(6)=4\)

如果某個正整數 \(x\) 滿足:

  • 對任意的 \(i(0<i<x)\) 滿足 \(g(x)>g(i)\),則稱 \(x\) 為反質數。
  • 例如,整數 \(1,2,4,6\) 等都是反質數。

現在給定一個數 \(N\),你能求出不超過 \(N\) 的最大的反質數麼?

輸入格式

一個數 \(N(1\leq N\leq 2\times 10^9)\)

輸出格式

不超過 \(N\) 的最大的反質數。

樣例

樣例輸入

1000

樣例輸出

840

資料範圍與提示

保證 \(25\%\) 的資料 \(1\leq N\leq 10^3\)

保證 \(50\%\) 的資料 \(1\leq N\leq 10^5\)

保證 \(75\%\) 的資料 \(1\leq N\leq 10^8\)

保證 \(100\%\) 的資料 \(1\leq N\leq 2\times 10^9\)

思路

打表進省一!!!

省選必備基礎——打表

咳咳,我打表混了 \(75opts\),要不是時間太少,後面幾個沒跑出來。

正解用一個約數和公式,就是約數和定理,直接 \(DFS\) 一遍即可。

程式碼

正解

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=1e5+50,INF=0x3f3f3f3f;

int prime[15]={0,2,3,5,7,11,13,17,19,23,29,31,37};//質數表
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n,mx,ans,s[maxn];

void DFS(int x,int sum,int maxx){
	if(x>12)return;
	if(sum>mx||(sum==mx&&maxx<ans)){//質數個數超過了之前記錄的最大值
		mx=sum;
		ans=maxx;
	}
	s[x]=0;
	while(maxx*prime[x]<=n&&s[x]<s[x-1]){//分解質因數
		s[x]++;
		maxx=maxx*prime[x];
		int w=sum*(s[x]+1);
		DFS(x+1,w,maxx);
	}
}
signed main(){
	n=read();
	s[0]=1000000;
	DFS(1,1,1);
	printf("%lld\n",ans);
	return 0;
}

打表

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e5+5,INF=0x3f3f3f3f;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
int n;
int a[100]={0,1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,554400,665280,720720,1081080,1441440,2162160,2882880,3603600,4324320,6486480,7207200,8648640,10810800,14414400,17297280,21621600,32432400,36756720,43243200,61261200,73513440,110270160,122522400,147026880,183783600,245044800,294053760,367567200,551350800,698377680,735134400,1102701600,1396755360};
int main(){
	n=read();
	cout<<a[upper_bound(a+1,a+1+68,n)-a-1]<<endl;
	return 0;
}

sam-Toy Cars

洛谷P3419

題目描述

\(Jasio\) 是一個三歲的小男孩,他最喜歡玩玩具了,他有 \(n\) 個不同的玩具,它們都被放在了很高的架子上,所以 \(Jasio\) 拿不到它們。為了讓他的房間有足夠的空間,在任何時刻地板上都不會有超過 \(k\) 個玩具(\(Jasio\) 在地板上玩玩具)。

\(Jasio\) 的媽媽則在房間裡陪他的兒子。當 \(Jasio\) 想玩地板上的其他玩具時,他會自己去拿。如果他想玩的玩具在架子上,他的媽媽則會幫他去拿。當她拿玩具的時候,順便也會將一個地板上的玩具放上架子使得地板上有足夠的空間。他的媽媽很清楚自己的孩子,所以他能夠預料到 \(Jasio\) 想玩些什麼玩具。所以她想盡量的使自己去架子上拿玩具的次數儘量的少,應該怎麼安排放玩具的順序呢?

輸入格式

第一行三個整數:\(n,k,p(1\leq k\leq n\leq 10^5,1\leq p\leq 5\times 10^5)\),分別表示玩具的總數、地板上玩具的最多個數以及 \(Jasio\) 他想玩玩具的序列的個數。

接下來 \(p\) 行每行描述一個玩具編號,表示 \(Jasio\) 想玩的玩具。

輸出格式

一個數表示 \(Jasio\) 的媽媽最少要拿多少次玩具。

樣例

樣例輸入

3 2 7
1
2
3
1
3
1
2

樣例輸出

4

資料範圍與提示

\(30\%\) 的資料滿足 \(1\leq n\leq 500,1\leq k\leq 200,1\leq p\leq 2000\)

\(50\%\) 的資料滿足 \(1\leq n\leq 5000,1\leq k\leq 2000,1\leq p\leq 40000\)

\(100\%\)的資料滿足 \(1\leq k\leq n\leq 100,000,1\leq p\leq 500,000\)

思路

很明顯的一個貪心,考試的時候直接 \(DFS\) 了一遍,混了 \(10opts\),其他的全 \(TLE\) 了。

很容易發現,當書本是 \(k\) 本時,需要一本來替換,由樣例,可以推出:在我們當前選中的 \(k\) 本書中,最後需要的書,要被替換到書架上。

所以維護一個大根堆,靠後出現的書本先被放到書架上。

有一個小技巧:用圖論存圖的方式,用前向星的方法來儲存,\(next[i]\) 表示第 \(i\) 次玩的玩具 \(a[i]\) 下一次玩的時間。

程式碼

#include <bits/stdc++.h>
using namespace std;

const int maxn=5e5+50,INF=0x3f3f3f3f;

inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n,k,p;
int ans;
int a[maxn],head[maxn],next[maxn],vis[maxn];
struct Node{
	int id,next;
	Node(int a,int b){
		id=a;
		next=b;
	}
	bool operator < (const Node &a) const{//維護大根堆
		return next<a.next;
	}
};
priority_queue<Node> q;

int main(){
	n=read(),k=read(),p=read();
	for(int i=1;i<=p;i++){
		a[i]=read();
		if(head[a[i]]!=0){//用鄰接表儲存
			next[head[a[i]]]=i;
		}
		head[a[i]]=i;
	}
	for(int i=1;i<=p;i++){
		if(next[i]==0){
			next[i]=p+1;
		}
	}
	for(int i=1;i<=p;i++){
		if(vis[a[i]]==1){
			q.push(Node(a[i],next[i]));
			continue;
		}
		if(k!=0){
			q.push(Node(a[i],next[i]));
			vis[a[i]]=1;
			k--;
			ans++;
		}else{
			while(vis[q.top().id]==0){
				q.pop();
			}
			vis[q.top().id]=0;
			q.pop();
			q.push(Node(a[i],next[i]));
			vis[a[i]]=1;
			ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
}