考試 - 2017.10.5


反正都是原題,不設密碼沒什么大不了的


T1

黑白棋游戲

描述

一個 4 × 4 的 0/1 矩陣

每次可以交換相鄰兩個元素

求從初始狀態到目標狀態的最小交換次數

輸入

前四行,每行一個長為 4 的 0/1 字符串,描述初始狀態

后四行,每行一個長為 4 的 0/1 字符串,描述目標狀態

輸出

一行一個數,表示最小交換次數

樣例

.in
1111
0000
1110
0010
1010
0101
1010
0101
1.5
.out
4

好像見過這題的強化版

https://www.luogu.org/problem/show?pid=1225 .
可以用狀壓哈希+搜索(就16個格子可以蛤希成int)

搜索的局面最多12870個,不怕超時

最好用位運算優化一下廢話

代碼蒯上

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gotcha()
{
    register int _a=0;bool _b=1;register char _c=getchar();
    while(_c<'0' || _c>'9'){if(_c=='-')_b=0;_c=getchar();}
    while(_c>='0' && _c<='9')_a=_a*10+_c-48,_c=getchar();
    return _b?_a:-_a;
}
using namespace std;
int bit[16],h[2][65537],step[2][65537],he[2]={0},ta[2],st,tar,s;
bool ed[2][65537]={0};
void BFS(int x)
{
    register int i,num=h[x][++he[x]],n;
    for(i=15;i>=0;i--)
    {
        if(i%4!=0)
        {
            int a=num&bit[i],b=num&bit[i-1];
            n=num+(a>>1)-a+(b<<1)-b;
            if(ed[x][n]==0)
            {
                h[x][++ta[x]]=n;
                ed[x][n]=1;
                step[x][n]=step[x][num]+1;
            } 
        }
        if(ed[0][n] && ed[1][n])printf("%d",step[0][n]+step[1][n]),exit(0);
        if(i>=4)
        {
            int a=num&bit[i],b=num&bit[i-4];
            n=num+(a>>4)-a+(b<<4)-b;
            if(ed[x][n]==0)
            {
                h[x][++ta[x]]=n;
                ed[x][n]=1;
                step[x][n]=step[x][num]+1;
            } 
        }
        if(ed[0][n] && ed[1][n])printf("%d",step[0][n]+step[1][n]),exit(0);
    }
}
int main()
{
    register int i;bit[0]=1;
    for(i=1;i<=15;i++)bit[i]=bit[i-1]<<1;
    for(i=15;i>=0;i--)scanf("%1d",&s),st+=s<<i;
    for(i=15;i>=0;i--)scanf("%1d",&s),tar+=s<<i;
    ta[0]=ta[1]=1;
    h[0][1]=st,h[1][1]=tar,ed[0][st]=1,ed[1][tar]=1;
    while(he[0]<ta[0] && he[1]<ta[1])BFS(0),BFS(1);
    return 0;
}

T2

王八棋

描述

n 個格子,每個格子上有個分數

m 張牌,每張牌上面一個 1 到 4 的整數

王八初始時在 1 號格子,每次你可以使用一張牌,使王八前進這張牌上的數字這么多個格子

一張牌只能用一次

總得分為王八到達的所有格子的分數和

求最大化總得分

n ≤ 350, m ≤ 120 ,每種牌的數目不超過 40 ,保證所有的牌用完后,王八停在 n 號格子

輸入

輸入文件的每行中兩個數之間用一個空格隔開

第 1 行 2 個正整數 n 和 M ,分別表示棋盤格子數和爬行卡片數

第 2 行 n 個非負整數,a_1, a_2, ..., a_n,其中 a i 表示棋盤第 i 個格子上的分數

第 3 行 m 個整數,b_1, b_2, ..., b_m,表示 m 張爬行卡片上的數字

輸入數據保證到達終點時剛好用光 m 張爬行卡片,即$ n-1=∑ mi=1 b i $

輸出

輸出只有 1 行,1 個整數,表示最多能得到的分數

樣例

.in
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
2.5
.out
73

這特么不是一DP題么

怎么跑這里來了

就4種牌,開個4維數組DP水過

不過要注意轉移時可能會溢出

代碼蒯上

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gotcha()
{
    register int _a=0;bool _b=1;register char _c=getchar();
    while(_c<'0' || _c>'9'){if(_c=='-')_b=0;_c=getchar();}
    while(_c>='0' && _c<='9')_a=_a*10+_c-48,_c=getchar();
    return _b?_a:-_a;
}
const int _ = 50;
int DP[_][_][_][_]={0},m,n,card[5]={0},maps[_*8]={0};
int main()
{
    memset(DP,0,sizeof(DP));
    register int i,j,k,l;
    n=gotcha(),m=gotcha();
    for(i=1;i<=n;i++)maps[i]=gotcha();
    for(i=1;i<=m;i++)card[gotcha()]++;
    for(i=0;i<=card[1];i++)
        for(j=0;j<=card[2];j++)
            for(k=0;k<=card[3];k++)
                for(l=0;l<=card[4];l++)
                    if(i+j*2+k*3+l*4+1<=n)
                        DP[i+5][j+5][k+5][l+5]=max(max(DP[i+4][j+5][k+5][l+5],DP[i+5][j+4][k+5][l+5]),max(DP[i+5][j+5][k+4][l+5],DP[i+5][j+5][k+5][l+4]))+maps[i+j*2+k*3+l*4+1];
    printf("%d",DP[card[1]+5][card[2]+5][card[3]+5][card[4]+5]);
    return 0;
}

T3

CF-177-A

描述

滿足以下條件的字符串:

  1. 包含恰好 k 個不同字符
  2. 長度為 n
  3. 相鄰兩個字符不同

    如果存在多個,請輸出字典序最小的

    不存在的話輸出-1

    n ≤ 10 6 , k ≤ 26

輸入

一行兩個數 n, k

輸出格式

一個長度為 n 的字符串

樣例輸入

.in
7 4
.out
ababacd

這是一道貪心題

加粗部分是出題人蒯題目沒有蒯完的地方

考試時並沒有看到,所以代碼就變成了這樣……

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gotcha()
{
    register int _a=0;bool _b=1;register char _c=getchar();
    while(_c<'0' || _c>'9'){if(_c=='-')_b=0;_c=getchar();}
    while(_c>='0' && _c<='9')_a=_a*10+_c-48,_c=getchar();
    return _b?_a:-_a;
}
int n,lim;
int main()
{
    register int i,j;
    n=gotcha(),lim=gotcha();
    if(lim>n){puts("Fuck you");return 0;}
    if(lim==1){puts(n==1?"a":"Fuck you");return 0;}
    else if(lim==2)for(i=1;i<=n;i++)putchar(i%2?'a':'b');
    else
    {
        for(i=1;i<=n-lim+2;i++)putchar(i%2?'a':'b');j='c';
        for(i=n-lim+3;i<=n;i++)putchar(char(j)),j++;
    }
    return 0;
}

T4

蟲食算

描述

給定一個用字母代替的加法等式

AB+BA=CC → 12+21=33

保證唯一解,求之。所有數字為 n 進制數,且加法為 n 進制下的加法

n ≤ 26

輸入

包含 4 行. 第一行有一個正整數 n ≤ 26

后面的 3 行每行有一個由大寫字母組成的字符串,分別代表兩個加數以及和。這 3 個字符串 左右兩端都沒有空格,從高位到低位,並且恰好有 n 位

輸出

包含一行。在這一行中,應當包含唯一的那組解。解是這樣表示的:輸出 n 個數字,分別表示A,B,C......所代表的數字,相鄰的兩個數字用一個空格隔開,不能有多余的空格

樣例

.in
4
BADC
CBDA
DCCC
.out
0 1 2 3

這就是真的一道原題了

要注意,這是K進制加法

並且要從后(右)往前(左)搜,可以使觸發剪枝的概率變高

代碼蒯上

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int _ = 28;
int n,l[_],r[_],sum[_],ans[_];
bool ed[_]={0};
void DFS(int x,int y,int up)
{
    int i,a,b,c,all;
    if(!x){for(i=0;i<n;i++)printf("%d ",ans[i]);exit(0);}
    for(i=1;i<=x;i++)
    {
        a=ans[l[i]],b=ans[r[i]],c=ans[sum[i]];
        if(a!=-1 && b!=-1 && c!=-1 && ((a+b+1)%n!=c && (a+b)%n!=c))return;
    }
    if(y==1)
    {
        if(ans[l[x]]>-1){DFS(x,2,up);return;}
        for(i=0;i<n;i++)
            if(!ed[i])ans[l[x]]=i,ed[i]=1,DFS(x,2,up),ans[l[x]]=-1,ed[i]=0;
        return;
    }
    a=ans[l[x]],b=ans[r[x]],c=ans[sum[x]];
    if(b>-1)
    {
        all=a+b+up;
        if(c==-1)
        {
            ans[sum[x]]=all%n,ed[ans[sum[x]]]=1;DFS(x-1,1,all/n);
            ans[sum[x]]=-1,ed[all%n]=0;
            return;
        }
        if(all%n==c)DFS(x-1,1,all/n);
        return;
    }
    for(i=n-1;i>=0;i--)
        if(!ed[i])
        {
            all=a+i+up;
            if((all%n!=c&&c>-1)||(c==-1&&ed[all%n]))continue;
            if(c==-1)ed[all%n]=1,ans[sum[x]]=all%n;
            ans[r[x]]=i,ed[i]=1,DFS(x-1,1,all/n),ans[r[x]]=-1,ed[i]=0;
            if(c==-1)ed[all%n]=0,ans[sum[x]]=-1;
        }
}
char s[_];
int main()
{
    memset(ans,-1,sizeof(ans));
    register int i;
    scanf("%d",&n);
    scanf("%s",s);for(i=1;i<=n;i++)l[i]=s[i-1]-'A';
    scanf("%s",s);for(i=1;i<=n;i++)r[i]=s[i-1]-'A';
    scanf("%s",s);for(i=1;i<=n;i++)sum[i]=s[i-1]-'A';
    DFS(n,1,0);
    return 0;
}

T5

單詞矩陣

描述

把 A 到 Y 的排列從上到下從左至右依次填入一個 5 × 5 的矩陣

如果每行每列都遞增,則稱這個排列是優美的

任務 1 :給定一個優美的排列,求這個排列是第幾個優美的排列

任務 2 :給定 k ,求第 k 個優美排列

輸入

第一行一個字母,W 表示任務 1,N 表示任務 2

若是任務 1,第二行是一個優美的排列,否則第二行是一個正整數,表示某個優美的排列的編 號,保證該數不超過優美的排列的總數

輸出

一行,若是任務 1,輸出對應編號,否則輸出對應的優美的排列

樣例

.in
W
ABCDEFGHIJKLMNOPQRSUTVWXY
.out
2

全場爆零之殤

這一題竟然也是搜索

按字典序搜索排列

位運算優化:S&(-(1<<t)) :刪去集合 S 中小於 t 的所有元素

剪枝:

  1. (i, j) 右下角的字符一定都比 (i, j) 的大
  2. 如果大於當前字符的數目填不滿右下角這個區域,無解

想象這是題目的所有解。

▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 

我們可以預處理打表,把一些特定的地方的解求出來。

比如像這樣

▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉
_♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_____♂_

(我暫時找不到向上箭頭,先用這個代替一下)

然后就可以在一個比原來小的多的區域求解了


沒有可用的較好的代碼



T6

CF-54-E

題目描述

給定一個(可能)不成立的等式:a+b=c ,要求插入盡量少的數字使得這個等式成立

2+4=5 ⇒ 21+4=25

1+1=3 ⇒ 1+31=32

保證 a, b, c 不存在前導零,要求插入后的等式中的三個數不允許存在前導零

a,b,c < 10^6

輸入

僅一行 a+b=c

輸出格式

一行表示成立的等式

樣例

.in
1+1=3
.out
1+31=32

為方便處理進位,從低位往高位搜

答案上界:12 位

結論:

  1. 如果當前搜素的最低位是合法的,就不需添加
  2. 如果不合法,則 只添加一個數位
  3. 枚舉在哪里添加

    還可以加上一些最優化剪枝

不是標程:這個代碼的得分是90

錯誤原因是:沒有SPJ。

// This file is created by XuYike's black technology automatically.
// Copyright (C) 2015 ChangJun High School, Inc.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long lol;
int gi(){
    int res=0,fh=1;char ch=getchar();
    while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
    if(ch=='-')fh=-1,ch=getchar();
    while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
    return fh*res;
}
const int MAXN=100001;
const int INF=1e9;
const int K=13;
lol a,b,c,sa,sb,d[K];
int gl=K;
void dfs(lol a,lol b,lol c,lol ga,lol gb,lol gc,int l,int p){
    if(l>=gl)return;
    if(!a&&!b&&!c&&!gc){sa=ga;sb=gb;gl=l;return;}
    if(!c){
        int k=0;
        for(lol tmp=a+b+gc;tmp;tmp/=10)k++;
        dfs(0,0,0,a*d[p]+ga,b*d[p]+gb,0,l+k,p);
        return;
    }
    if((a+b+gc)%10==c%10)dfs(a/10,b/10,c/10,d[p]*(a%10)+ga,d[p]*(b%10)+gb,(a%10+b%10+gc)/10,l,p+1);
    else{
        dfs(a*10+(c+10-b%10-gc)%10,b,c,ga,gb,gc,l+1,p);
        dfs(a,b*10+(c+10-a%10-gc)%10,c,ga,gb,gc,l+1,p);
        dfs(a,b,c*10+(a+b+gc)%10,ga,gb,gc,l+1,p);
    }
}
int main(){
    scanf("%lld+%lld=%lld",&a,&b,&c);
    d[0]=1;for(int i=1;i<K;i++)d[i]=d[i-1]*10;
    dfs(a,b,c,0,0,0,0,0);
    printf("%lld+%lld=%lld",sa,sb,sa+sb);
    return 0;
}

T7

最大團

描述

給定一個圖 tt = (V, E)

求一個點集 S ,使得對於任意 x ≠ y ∈ S ,x 和 y 都有一條邊

|V | ≤ 50

輸入

第一行兩個數,n, m 分別表示圖的點數、邊數。 接下來 m 行,每行兩個整數 x, y 表示一條邊 x ↔ y

輸出格式

輸出最大團的大小以及最大團的數目

樣例

.in
4 5
1 2
2 3
3 1
1 4
2 4
.out
3 2

還是一道搜索……

  1. 位運算優化

2.倒着搜:

令 f i 表示后 i 個點所組成的圖的最大團

從 f n 開始倒着求 f 1

在求解 f i 時,當前最優解是 best ,搜到了點 x ,且 i 到 x − 1 中選擇了 k個點

若 k + f x ≤ best ,則繼續搜下去不可能更新答案,剪枝

// This file is created by GuTingFeng.
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<ctime>
#include<vector>
#include<stack>
#include<set>
#include<map>
#ifdef WIN32
#define LLD "%I64d"
#else
#define LLD "%lld"
#endif
using namespace std;

typedef long long LL;

const int INF=2147483647;

LL getint()
{
    LL res=0,p=1;
    char ch=getchar();
    while ((ch<'0'||ch>'9') && ch!='-') ch = getchar();
    if (ch=='-') p=-1,ch=getchar();
    while (ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*p;
}

bool g[55][55];
int n,m,mx[55],w[55][55],ans,cnt;

bool dfs(int now, int sum) {
    if(now==0) {
        if (sum==ans) {
            cnt++;
        }
        if (sum>ans) {
            ans=sum;
            cnt=1;
            return 1;
        }
        return 0;
    }
    for(int i=0;i<now;i++) {
        
        int u=w[sum][i];
        if(mx[u]+sum<ans) return 0; //后面的最大團+當前最大團比ans小
        int nxt=0;
        for(int j=i+1; j<now; j++)
            if(g[u][w[sum][j]]) w[sum+1][nxt++]=w[sum][j];
        dfs(nxt,sum+1);
    }
    return 0;
}

int mxc() {
    ans=0;
    memset(mx,0,sizeof(mx));
    for(int i=n-1;i>=0;i--) {
        int now=0;
        for(int j=i+1;j<n;j++) if(g[i][j]) w[1][now++]=j;
        dfs(now,1);
        mx[i]=ans;
    }
    return ans;
}

int main()
{
    n=getint();
    m=getint();
    int i,u,v;
    for (i=1;i<=m;i++) {
        u=getint()-1;
        v=getint()-1;
        g[u][v]=g[v][u]=1;
    }
    mxc();
    printf("%d %d\n",ans,cnt);
    return 0;
}

T8

TC-572-D1L2

描述

有一個神秘的常數 K ,s 位

現在有 n 個 s 位數,告訴你每個數與 K 有多少位是相同的

判斷 K 的無解、多解、唯一解,並求出唯一解(如果存在的話)

所有出現的數都允許前導零

s ≤ 9, n ≤ 50

輸入

第一行兩個數 n, s

接下來 n 行,每行兩個數 a, b 表示 s 位數 a 與 K 有 b 位是相同的

輸出

無解輸出 Liar , 多解輸出 Ambiguity ,唯一解則輸出唯一解

樣例

.in
5 4
1234 2
4321 1
1111 1
3333 2
7777 1
.out
1337

一般的搜索肯定是超時的

這里要用到雙向搜索

先枚舉最后 \(t=floor(s/2)\) 位,求出所有可能的后 t 位與 K 的相同位數

搜索前 s − t 位,利用 hash 判斷是否存在一個“后 t 位”


## *沒有可用的較好的代碼*


T9

方程的解數

描述

已知一個n元高次方程:
$ k_1x_1^{p_1} + k_2x_2^{p_2} + \cdots + k_nx_n^{p_n} = 0 $

其中:x1, x2, …,xn是未知數,k1,k2,…,kn是系數,p1,p2,…pn是指數。且方程中的所有數均為整數

求有多少組整數解

輸入

文件的第 1 行包含一個整數 n

第 2 行包含一個整數 m 。第 3 行到第 n + 2 行,每行包含兩個整數,分別表示 k i 和 p i 。兩 個整數之間用一個空格隔開。第 3 行的數據對應 i = 1 ,第 n + 2 行的數據對應 i = n

輸出

文件僅一行,包含一個整數,表示方程的整數解的個數

樣例

.in
3
150
1 2
-1 2
1 2
9.5
.out
178

也是雙向搜索

枚舉前 3 個 x 的值,存入 Hash 表

再枚舉后 3 個 x 的值,可以算出前三個的和,在 Hash 中查詢這個和出現的次數

不是標程:這個代碼的得分是90

錯誤原因是:TLE。

// This file is created by ZouKeShen.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 3500000 //150^3
int n, m, ans;
int p[20], k[20];

int res1[MAXN], rk1;
int res2[MAXN], rk2;

int p1[5], k1[5];
int ksm(int d,int z)//快速冪,稍微加快一點
{
	int res=1;
	for (;z>0;d*=d,z>>=1)
		if (z&1)
			res=res*d;
	return res;
}

int lv;
void dfs(int i, int res, int*a, int&rk) //最后兩個參數的設置可以讓兩組DFS共享一個函數
{
	if (i>lv) { a[++rk]=res; return; }
	for (int x=1; x<=m;++x)
		dfs(i+1,res+k1[i]*ksm(x,p1[i]),a,rk);
}

void work()
{
	int i, j=rk2;
	int cnt1, cnt2;
	sort(res1+1, res1+rk1+1);
	sort(res2+1, res2+rk2+1);
	//這個循環中根據單調性,i不降,j不增,所以總的時間復雜度為O(n)
	for(i = 1; i<=rk1; ++i) {
		while (res1[i]+res2[j]>0&&j>0) --j; //當i, j越小,res1[i]+res2[j]越小。i增加后,j的取值應單調減小。
		if(j<=0) break;
		if(res1[i]+res2[j] != 0)continue;
		cnt1=cnt2 =1;
		while(res1[i+1]==res1[i]&&i<rk1) ++i,++cnt1;
		while(res2[j-1]==res2[j]&&j>1) --j,++cnt2;
		ans = ans + cnt1 * cnt2; // 乘法原理
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	int i;
	int part1=n/2, part2=(n+1)/2; //分別為下取整,上取整。
	for (i=1;i<=n;++i)
		scanf("%d%d",k+i,p+i);
	for (i=1;i<=part1;++i)
		p1[i]=p[i],k1[i]=k[i];
	lv=part1;
	dfs(1,0,res1,rk1);
	for (i=1;i<=part2;++i)
		p1[i]=p[i+part1],k1[i]=k[i+part1];
	lv=part2;
	dfs(1,0,res2,rk2);
	work();
	printf("%d\n",ans);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM