【紀中2020.4.22日】模擬賽題解

目錄:

T1:潛伏者
T2:Hankson的趣味題
T3:最優貿易
T4:靶形數獨

這次的題是洛谷上的題……不是USACO了

正題:

T1:潛伏者

題目描述

     R 國和S 國正陷入戰火之中,雙方都互派間諜,潛入對方內部,伺機行動。
     歷盡艱險後,潛伏於 S 國的R 國間諜小C 終於摸清了S 國軍用密碼的編碼規則:
     1. S 國軍方內部欲發送的原信息經過加密後在網絡上發送,原信息的內容與加密後所得的內容均由大寫字母‘A’-‘Z’構成(無空格等其他字符)。
     2. S 國對於每個字母規定了對應的“密字”。加密的過程就是將原信息中的所有字母替換爲其對應的“密字”。
     3. 每個字母只對應一個唯一的“密字”,不同的字母對應不同的“密字”。“密字”可以和原字母相同。
     例如,若規定‘A’的密字爲‘A’,‘B’的密字爲‘C’(其他字母及密字略),則原信息“ABA”被加密爲“ACA”。
     現在,小 C 通過內線掌握了S 國網絡上發送的一條加密信息及其對應的原信息。小C希望能通過這條信息,破譯S 國的軍用密碼。小C 的破譯過程是這樣的:掃描原信息,對於原信息中的字母x(代表任一大寫字母),找到其在加密信息中的對應大寫字母y,並認爲在密碼裏y 是x 的密字。如此進行下去直到停止於如下的某個狀態:
     1. 所有信息掃描完畢,‘A’-‘Z’ 所有 26 個字母在原信息中均出現過並獲得了相應的“密字”。
     2. 所有信息掃描完畢,但發現存在某個(或某些)字母在原信息中沒有出現。
     3. 掃描中發現掌握的信息裏有明顯的自相矛盾或錯誤(違反S 國密碼的編碼規則)。例如某條信息“XYZ”被翻譯爲“ABA”就違反了“不同字母對應不同密字”的規則。
     在小 C 忙得頭昏腦漲之際,R 國司令部又發來電報,要求他翻譯另外一條從S 國剛剛截取到的加密信息。現在請你幫助小C:通過內線掌握的信息,嘗試破譯密碼。然後利用破譯的密碼,翻譯電報中的加密信息。

輸入

輸入文件名爲 spy.in,共3 行,每行爲一個長度在1 到100 之間的字符串。

第 1 行爲小C 掌握的一條加密信息。
第 2 行爲第1 行的加密信息所對應的原信息。
第 3 行爲R 國司令部要求小C 翻譯的加密信息。
輸入數據保證所有字符串僅由大寫字母‘A’-‘Z’構成,且第1 行長度與第2 行相等。

輸出

輸出文件 spy.out 共1 行。

若破譯密碼停止時出現 2,3 兩種情況,請你輸出“Failed”(不含引號,注意首字母大寫,其它小寫)。

否則請輸出利用密碼翻譯電報中加密信息後得到的原信息。

樣例輸入

Sample Input1:

AA

AB

EOWIE

Sample Input2:

QWERTYUIOPLKJHGFDSAZXCVBN

ABCDEFGHIJKLMNOPQRSTUVWXY

DSLIEWO

Sample Input3:

MSRTZCJKPFLQYVAWBINXUEDGHOOILSMIJFRCOPPQCEUNYDUMPP

YIZSDWAHLNOVFUCERKJXQMGTBPPKOIYKANZWPLLVWMQJFGQYLL

FLSO

樣例輸出

Sample Output1:

Failed

【輸入輸出樣例 1 說明】

原信息中的字母‘A’和‘B’對應相同的密字,輸出“Failed”

Sample Output2:

Failed

【輸入輸出樣例2 說明】

字母‘Z’在原信息中沒有出現,輸出“Failed”。

Sample Output3:

NOIP

分析:

輸入
再檢查是否failed(重點)
(1)輸入的字符串是否保證26個字母都有自己的密碼
(2)判斷是否一對一
最後使用三個數組,一個數組做標記,一個做比較,一個輸出
要做一些字符串轉換之類的

CODE:

#include<cstdio>
#include<cstring>
char fir[101],sec[101],tr[101];
char bol[101],bol1[101];
int flag[101];
int len;
int main() 
{
	freopen("spy.in","r",stdin);
	freopen("spy.out","w",stdout);
	scanf("%s%s%s",&fir,&sec,&tr);
	memset(flag,0,sizeof(flag));
	len=strlen(fir);
	if(len<26)
	{
		printf("Failed");
		return 0;
	} 
	for(int i=0;sec[i];i++)  //判斷是否出現一對多的情況
	
	{ 
		if(flag[sec[i]-'A']!=1){
			bol[sec[i]-'A']=fir[i];  //都是要字符轉換
			flag[sec[i]-'A']=1;  //標記
			bol1[fir[i]-'A']=sec[i]; 
		}
		else
		{
			if(bol[sec[i]-'A']!=fir[i])
			{
				printf("Failed");
				return 0;
			}	
		}	
	}
	for(int i=0;tr[i];i++)
		printf("%c",bol1[tr[i]-'A']);  //轉換字符輸出
	return 0;
}

T2: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 的個數。請你幫助他編程求解這個問題。

輸入

輸入文件名爲 son.in。第一行爲一個正整數n,表示有n 組輸入數據。接下來的n 行每行一組輸入數據,爲四個正整數a0,a1,b0,b1,每兩個整數之間用一個空格隔開。輸入數據保證a0 能被a1 整除,b1 能被b0 整除。

輸出

輸出文件 son.out 共n 行。每組輸入數據的輸出結果佔一行,爲一個整數。
對於每組數據:若不存在這樣的 x,請輸出0;
若存在這樣的 x,請輸出滿足條件的x 的個數;

樣例輸入

2
41 1 96 288
95 1 37 1776

樣例輸出

6
2

分析:

如果gcd(a,b)=k,那麼gcd(a/k,b/k)=1
然後就知道 gcd(b1/b0,b1/x)=1
gcd(x/a1,a0/a1)=1

CODE:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
LL gcd(LL x,LL y)
{
    if(y%x==0)
    {
        return x;
    }
    else
    {
        return gcd(y,x%y);  //輾轉相除法
    }
}
LL lcm(LL x,LL y)
{
    return x/gcd(x,y)*y;
}
int main()
{
	freopen("son.in","r",stdin);
	freopen("son.out","w",stdout);
    LL n=0;
    scanf("%lld",&n);
    while(n--)
    {
        LL ans=0,ax=0,ay=0,bx=0,by=0;
        scanf("%lld%lld%lld%lld",&ax,&ay,&bx,&by);
        LL t=(LL)sqrt((LL)by+1);
        for(LL i=1;i<=t;i++)
        {
            if(by%i==0)
            {
                if(gcd(i,ax)==ay&&lcm(i,bx)==by)  //上面公式
                {
                    ans++;  //計數
                }
                if(i!=by/i)
                {
                    if(gcd(by/i,ax)==ay&&lcm(by/i,bx)==by)  //上面推出的
                    {
                        ans++; //計數
                    }
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

T3:最優貿易

這道題我之前在學校做過,洛谷的最短路題目……
我還寫了題解…做兩遍spfa,建反圖……
題目傳送門

題解傳送門

T4:靶形數獨

題目描述

     小城和小華都是熱愛數學的好學生,最近,他們不約而同地迷上了數獨遊戲,好勝的他們想用數獨來一比高低。但普通的數獨對他們來說都過於簡單了,於是他們向Z 博士請教,Z 博士拿出了他最近發明的“靶形數獨”,作爲這兩個孩子比試的題目。

    靶形數獨的方格同普通數獨一樣,在 9 格寬×9 格高的大九宮格中有9 個3 格寬×3 格高的小九宮格(用粗黑色線隔開的)。在這個大九宮格中,有一些數字是已知的,根據這些數字,利用邏輯推理,在其他的空格上填入1 到9 的數字。每個數字在每個小九宮格內不能重複出現,每個數字在每行、每列也不能重複出現。但靶形數獨有一點和普通數獨不同,即每一個方格都有一個分值,而且如同一個靶子一樣,離中心越近則分值越高。(如圖)





    上圖具體的分值分佈是:最裏面一格(黃色區域)爲 10 分,黃色區域外面的一圈(紅色區域)每個格子爲9 分,再外面一圈(藍色區域)每個格子爲8 分,藍色區域外面一圈(棕色區域)每個格子爲7 分,最外面一圈(白色區域)每個格子爲6 分,如上圖所示。比賽的要求是:每個人必須完成一個給定的數獨(每個給定數獨可能有不同的填法),而且要爭取更高的總分數。而這個總分數即每個方格上的分值和完成這個數獨時填在相應格上的數字的乘積的總和。如圖,在以下的這個已經填完數字的靶形數獨遊戲中,總分數爲2829。遊戲規定,將以總分數的高低決出勝負。




    由於求勝心切,小城找到了善於編程的你,讓你幫他求出,對於給定的靶形數獨,能夠得到的最高分數。

輸入

輸入文件名爲 sudoku.in。
一共 9 行。每行9 個整數(每個數都在0—9 的範圍內),表示一個尚未填滿的數獨方格,未填的空格用“0”表示。每兩個數字之間用一個空格隔開。

輸出

輸出文件 sudoku.out 共1 行。
輸出可以得到的靶形數獨的最高分數。如果這個數獨無解,則輸出整數-1。

樣例輸入

Sample Input1:

7 0 0 9 0 0 0 0 1

1 0 0 0 0 5 9 0 0

0 0 0 2 0 0 0 8 0

0 0 5 0 2 0 0 0 3

0 0 0 0 0 0 6 4 8

4 1 3 0 0 0 0 0 0

0 0 7 0 0 2 0 9 0

2 0 1 0 6 0 8 0 4

0 8 0 5 0 4 0 1 2

Sample Input2:

0 0 0 7 0 2 4 5 3

9 0 0 0 0 8 0 0 0

7 4 0 0 0 5 0 1 0

1 9 5 0 8 0 0 0 0

0 7 0 0 0 0 0 2 5

0 3 0 5 7 9 1 0 8

0 0 0 6 0 1 0 0 0

0 6 0 9 0 0 0 0 1

0 0 0 0 0 0 0 0 6

樣例輸出

Sample Output1:

2829

Sample Output2:

2852

分析:

首先要初始化三個數組:行、列、大宮格 用於判重
之後的思路就很清晰了,直接dfs
有一點值得注意,就是大宮格的索引推導
從上到下,從左到右的順序
用手畫一下可以推出 (x,y) 元素位於第 (x-1)/3*3+(y-1)/3+1 個大宮格上
與普通數獨不同的是,靶形數獨需要計算分數
我們可以簡單的推出簡化的計算公式
再往內部就每一層再加多一次
即:

int total = 2430+board[5][5];

然後因爲數獨可能有多解,因此直接深搜可能會超時,剪枝也難以進行,可以想到自己平時做數獨的時候都是從給定數字多的一行或一列開始入手,即深搜要有一個優先級

這時我們可以再初始化兩個數組,一個是每行的給定數字個數,另一個是數組下標,那麼我們可以通過對數組下標排序得到深搜的優先級

CODE:

#include<iostream>
#include<cmath>
#include<cstdio> 
#include<algorithm>
using namespace std;
int board[10][10];
int r[10][10],c[10][10],b[10][10],z[10],u[10];//行、列、大宮格判重數組 和 每行的給定數字的數組及其數組下標
int ans=-1;
bool cmp(int x,int y)//重載cmp
{
    return u[x]>u[y];
}
void cal() //簡化計算公式
{
    int total=2430+board[5][5];
    for(int i=2;i<=8;i++)
    {
        for(int j=2;j<=8;j++)
        {
            if (i>=4&&i<=6&&j>=4&&j<=6) total+=board[i][j]*3;
            else if (i>=3&&i<=7&&j>=3&&j<=7) total+=board[i][j]*2;
            else total+=board[i][j];
        }
    }
    ans=max(ans,total);
}
void dfs(int x,int y)  //優先級深搜,把原來的x改成z[x]
{
    if(board[z[x]][y])
    {
        if (x==9&&y==9) cal();
        if (y==9) dfs(x+1,1);
        else dfs(x,y+1);
    }
    else
    {
        for(int i=1;i<=9;i++)
        {
            if(r[z[x]][i]==0&&c[y][i]==0&&b[(z[x]-1)/3*3+(y-1)/3+1][i]==0)
            {
                r[z[x]][i]=1;
                c[y][i]=1;
                b[(z[x]-1)/3*3+(y-1)/3+1][i]=1;
                board[z[x]][y]=i;
                if (x==9&&y==9) cal();
                if (y==9) dfs(x+1,1);
                else dfs(x,y+1);
                board[z[x]][y]=0;
                r[z[x]][i]=0;
                c[y][i]=0;
                b[(z[x]-1)/3*3+(y-1)/3+1][i]=0;
            }
        }
    }
}

int main()
{
	freopen("sudoku.in","r",stdin);
	freopen("sudoku.out","w",stdout);
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
        {
            cin>>board[i][j];
            if(board[i][j])
            {
                r[i][board[i][j]]=1;
                c[j][board[i][j]]=1;
                b[(i-1)/3*3+(j-1)/3+1][board[i][j]]=1;
                u[i]++;
            }
        }
        z[i]=i;
    }
    sort(z+1,z+10,cmp);
    dfs(1,1);
    cout<<ans<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章