1. 程式人生 > 其它 >牛客IOI周賽25-普及組

牛客IOI周賽25-普及組

比賽連結

牛客IOI周賽25-普及組

B.學姐的編碼1.0

題目描述

學姐最近喜歡上了編碼,尤其是十六進位制編碼,但是學姐特別挑剔,在學姐眼中,只有逐位遞增的編碼才是一個優美的編碼,比如12,58都是優美的編碼,85,22則都不是優美的編碼,現在學姐得到了一個編碼串,她希望你告訴她該編碼串裡可查詢到的所有不重複的優美的編碼總個數,對於單個字元組成的編碼,學姐總是認為這個編碼是優美的,且優美的編碼當中是允許存在前導零的

編碼可查詢的判定依據:在給定編碼串\(s\)中刪去任意kk位字元\((0≤k<|s|)\),剩下字元不改變順序組成一個新的編碼\(s1\),則認為\(s1\)可在\(s\)中查詢到,如0102中可查詢的編碼有0,1,2,00,01,02,10,12,010,012,002,102,0102

輸入描述:

一個字串s表示所給十六進位制編碼串(1≤|s|≤1000000)
(保證所給編碼串與標準十六進位制一致,編碼串中僅可能出現0-9與A-F,不會有多餘字元出現)

輸出描述:

一個數,表示不重複計算的情況下優美的編碼的總個數

示例1

輸入

001A

輸出

7

說明

七種方案分別為
0,01,0A,01A,1,1A,A
備註:
對於10%的資料:1≤n≤5
對於30%的資料:1≤n≤10
對於100%的資料:1≤n≤1000000,其中另有10%保證字串中0-9與A-F至少各出現一次

解題思路

思維,dp

  • 狀態表示:\(f[i]\) 表示以字元 \(i\) 結尾的方案數

  • 狀態計算:\(f[i]=1+ \sum_{j=0}^{i-1}f[j]\)

每當遇到一個字元 \(i\),算上本身 \(1\) 的貢獻,再加上 \(0\sim i-1\) 的貢獻,\(\color{red}{為什麼這樣是正確的?}\)當字元 \(i\) 只出現一次時,這個性質是顯然的,當出現多次時,由於該字元前面還有該字元,中間會出現其他字元,如果出現小於當前字元的情況,原來的 \(f[i]\) 就不正確了,需要重新計算

  • 時間複雜度:\(O(16n)\)

程式碼

// Problem: 學姐的編碼1.0
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/32598/B
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=20;
LL f[N];
bool cnt[N];
int get(char c)
{
	if(c>='0'&&c<='9')return c-'0';
	else
		return c-'A'+10;
}
int main()
{
    string s;
    cin>>s;
    for(char c:s)
    {
    	int i=get(c);
    	f[i]=1;
    	for(int j=0;j<i;j++)f[i]+=f[j];
    }
    LL res=0;
    for(int i=0;i<16;i++)res+=f[i];
    cout<<res;
    return 0;
}

C.學姐的編碼2.0

題目描述
學姐最近喜歡上了編碼,尤其是十六進位制編碼,但是學姐特別挑剔,在學姐眼中,只有逐位遞增的編碼才是一個優美的編碼,比如12,58都是優美的編碼,85,22則都不是優美的編碼,現在學姐得到了一個編碼串,她希望你按照字典序從小到大輸出該編碼串裡可查詢到的所有不重複的優美的編碼,對於單個字元組成的編碼,學姐總是認為這個編碼是優美的,且優美的編碼當中是允許存在前導零的

編碼可查詢的判定依據:在給定編碼串ss中刪去任意k位字元\((0≤k<|s|)\),剩下字元不改變順序組成一個新的編碼\(s1\),則認為\(s1\)可在\(s\)中查詢到,如0102中可查詢的編碼有0,1,2,00,01,02,10,12,010,012,002,102,0102

輸入描述:

一個字串s表示所給十六進位制編碼串(1≤|s|≤1000000)
(保證所給編碼串與標準十六進位制一致,編碼串中僅可能出現0-9與A-F,不會有多餘字元出現)

輸出描述:

按字典序從小到大輸出所有滿足條件的編碼

示例1

輸入

001A

輸出

0
01
01A
0A
1
1A
A

示例2

輸入

00A1

輸出

0
01
0A
1
A

備註:

對於10%的資料:1≤n≤5
對於30%的資料:1≤n≤10
對於100%的資料:1≤n≤1000000,其中另有10%保證字串中0-9與A-F至少各出現一次

解題思路

dfs

D.迷陣

題目描述

學姐無意間走入了一個迷陣,迷陣可以看成一個 \(n \times n\) 的矩陣,每個位置有一個屬性 \(w_{i j}\) ,學姐每次只能往相鄰格點 移動,每個格點都可以被經過\(0\)次或任意次,已知對於兩個格點 \(\left(x_{i}, y_{i}\right)\left(x_{j}, y_{j}\right)\) ,當且僅當 \(\left|x_{i}-x_{j}\right|+\left|y_{i}-y_{j}\right|=1\) 時認為這兩點相鄰,學姐打算從 \((1,1)\) 走到 \((n, n)\) ,但學姐走出迷陣後一段時間內會精神混亂,精神混亂的時間取決於 學姐在迷陣中所經過的所有格點 (包括起點和終點) 的權值的極差(最大值減去最小值)。現在學姐想知道走出迷宮精神混亂時間 \(T\) 最少是多少。

輸入描述:

輸入描述:

第一行一個正整數n(n≤100),表示迷陣大小
接下來n行,每行n個數,表示wij(0≤wij≤3000)

輸出描述:

一個數,表示走到(n,n)時得到的最小的T

示例1

輸入

5
1 1 3 6 8
1 2 2 5 5
4 4 0 3 3
8 0 2 3 4
4 3 0 2 1

輸出

2

說明

1->1->2->2->0->2->0->2->1
路徑上最大值M1=2,最小值M2=0,T=M1-M2=2
顯然路徑方案不唯一

備註:

對於15%的資料: 1≤n≤20 0≤wij≤100
對於45%的資料: 1≤n≤100 0≤wij≤100,其中有另5%保證1≤n≤100 0≤wij≤5
對於100%的資料: 1≤n≤100 0≤wij≤3000

解題思路

二分

假設當前極差為 \(x\),遍歷最小值,如果存在這樣一條滿足範圍內的路徑,則說明該答案可能比當前極差小,否則不存在只能增大極差,擴大最小值和最大值的範圍了,正好對應二分
另外,由於一個格點可以經過0次或任意次,只要找到這樣一條路徑即可,所以不需要回溯

  • 時間複雜度:\(O(n^3log(3000))\)

程式碼

// Problem: 迷陣
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/32598/D
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=105;
int n,a[N][N],dx[]={-1,0,1,0},dy[]={0,1,0,-1};
bool f,v[N][N];
void dfs(int x,int y,int down,int up)
{
	if(x==n&&y==n)
	{
		f=true;
		return ;
	}
	for(int i=0;i<4;i++)
	{
		int nx=x+dx[i],ny=y+dy[i];
		if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&(!v[nx][ny])&&a[nx][ny]>=down&&a[nx][ny]<=up)
		{
			v[nx][ny]=true;
			dfs(nx,ny,down,up);
		}
	}
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    	for(int j=1;j<=n;j++)cin>>a[i][j];
    int l=0,r=3000;
    while(l<r)
    {
    	int mid=l+r>>1;
    	f=false;
    	for(int i=0;mid+i<=3000;i++)
    	{
    		int j=mid+i;
    		if(a[1][1]>=i&&a[1][1]<=j)
    		{
                memset(v,0,sizeof v);
    			v[1][1]=true;
    			dfs(1,1,i,j);
    			if(f)break;
    		}
    	}
    	if(f)r=mid;
    	else
    		l=mid+1;
    }
    cout<<l;
    return 0;
}