2020.11.24 考試題解
阿新 • • 發佈:2020-12-04
搜尋專題
T1黑白棋遊戲
尋找最少步數,一看就知道是用廣搜。 (雖然我考場上用的時深搜+迭代加深)
每次只能與上下左右的交換,如果直接暴力列舉,肯定超時,考慮優化。
<1> 如果有一個點和我們要交換的點顏色一樣,就沒必要換了。
<2> 我們可以用狀壓,將每次的判斷從\(O(16)\)變為\(O(1)\)。
有這些就可以了,要想更進一步,可以用雙向搜尋(不會,之後有空再補)。
#include<bits/stdc++.h> using namespace std; #define il inline #define vocaloid(v) (v>='0'&&v<='9') template <typename T> il void read(T &x) { x=0;char v=getchar(); while(!vocaloid(v)) v=getchar(); while(vocaloid(v)) {x=(x<<1)+(x<<3)+v-'0';v=getchar();} } int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; int a[5][5],b[5][5],vis[(1<<16)-1],fa[(1<<16)-1]; int res[100039][4]; int Beg,End,IA; struct Ans{ int x,y,xx,yy,fa; }ans[100039]; queue<int>q; il void init()//讀入 { char c; for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) { cin>>c; a[i][j]=c-'0'; } for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) { cin>>c; b[i][j]=c-'0'; } } il int GetDeci(int x[5][5])//轉成壓縮的狀態 { int comb=0,IA=0; for(int i=4;i>=1;i--) for(int j=4;j>=1;j--) { comb+=x[i][j]*pow(2,IA); IA++; } return comb; } il void change(int x,int a[5][5])//根據狀態轉回棋盤 { while(x) { for(int i=4;i>=1;i--) for(int j=4;j>=1;j--) { a[i][j]=x%2; x/=2; } } } il bool check(int x,int y,int xx,int yy) { if(xx<1||xx>4||yy<1||yy>4||a[x][y]==a[xx][yy]) return 0; else return 1; } il void bfs() { q.push(Beg);vis[Beg]=1; while(!q.empty()) { int x=q.front();q.pop(); change(x,a); for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) { int x=i,y=j; for(int k=0;k<=3;k++) { int xx=x+dx[k],yy=y+dy[k]; int flag=0; if(check(x,y,xx,yy)) { flag=1; int now_Deci=GetDeci(a); swap(a[x][y],a[xx][yy]); int Deci=GetDeci(a); if(!vis[Deci]) { vis[Deci]=1; ans[Deci]=(Ans){x,y,xx,yy,now_Deci}; fa[Deci]=now_Deci; q.push(Deci); } if(Deci==End) return ; } if(flag) swap(a[x][y],a[xx][yy]); } } } } int main() { freopen("game.in","r",stdin); freopen("game.out","w",stdout); init(); Beg=GetDeci(a),End=GetDeci(b); bfs(); fa[Beg]=0; while(fa[End]) { IA++; res[IA][0]=ans[End].x,res[IA][1]=ans[End].y,res[IA][2]=ans[End].xx,res[IA][3]=ans[End].yy; End=fa[End]; } printf("%d\n",IA); for(int i=IA;i>=1;i--) printf("%d%d%d%d\n",res[i][0],res[i][1],res[i][2],res[i][3]); return 0; }
T2烏龜棋
對於這道題,我們可以打出一個非常暴力的暴力,時間複雜度大概為\(O(m^m)\),程式碼如下:
il void dfs(int pos,int sum)
{
if(pos==n)
{
ans=max(ans,sum);
return ;
}
for(int i=1;i<=m;i++)
{
if(!vis[i])
{
vis[i]=1;
dfs(pos+card[i],sum+a[pos+card[i]]);
vis[i]=0;
}
}
}
肯定超時,所以我們需要優化。
我們注意到題目中說只有\(1,2,3,4\)四種卡牌,且每張不超過\(50\)
我們不必關心這契卡牌是以怎樣的順序使用的,反正最後都會走到同一個位置,所以我們可以用記憶化搜尋或動規,轉移方程為:
\[f_{i,j,p,q}=\max{(f_{i-1,j,p,q},f_{i,j-1,p,q},f_{i,j,p-1,q},f_{i,j,p,q-1})}+a_{now} \]\[now=1+1\times i+2\times j+3\times p+4\times q \]接下來就可以直接\(dp\)
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define vocaloid(v) (v>='0'&&v<='9')
template <typename T>
il void read(T &x)
{
x=0;char v=getchar();
while(!vocaloid(v)) v=getchar();
while(vocaloid(v)) {x=(x<<1)+(x<<3)+v-'0';v=getchar();}
}
int n,m,f[59][59][59][59];
int a[399],g[7];
il int dfs(int i,int j,int p,int q)//記憶化搜尋
{
if(f[i][j][p][q]) return f[i][j][p][q];
if(i==g[1]&&j==g[2]&&p==g[3]&&q==g[4]) return 0;
int x=1+1*i+2*j+3*p+4*q;
int Max=0;
if(i<g[1]) Max=max(Max,dfs(i+1,j,p,q)+a[x+1]);
if(j<g[2]) Max=max(Max,dfs(i,j+1,p,q)+a[x+2]);
if(p<g[3]) Max=max(Max,dfs(i,j,p+1,q)+a[x+3]);
if(q<g[4]) Max=max(Max,dfs(i,j,p,q+1)+a[x+4]);
return f[i][j][p][q]=Max;
}
int main()
{
freopen("tortoise.in","r",stdin);
freopen("tortoise.out","w",stdout);
read(n),read(m);int ser;
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=m;i++)
read(ser),g[ser]++;
/*f[0][0][0][0]=a[1];//動態規劃
for(int i=0;i<=g[1];i++)
for(int j=0;j<=g[2];j++)
for(int p=0;p<=g[3];p++)
for(int q=0;q<=g[4];q++)
{
if(i==0&&j==0&&p==0&&q==0) continue ;
int Max=0,x=1+1*i+2*j+3*p+4*q;
if(i>0) Max=max(Max,f[i-1][j][p][q]+a[x]);
if(j>0) Max=max(Max,f[i][j-1][p][q]+a[x]);
if(p>0) Max=max(Max,f[i][j][p-1][q]+a[x]);
if(q>0) Max=max(Max,f[i][j][p][q-1]+a[x]);
f[i][j][p][q]=max(Max,f[i][j][p][q]);
}
printf("%d\n",f[g[1]][g[2]][g[3]][g[4]]);*/
printf("%d\n",dfs(0,0,0,0)+a[1]);
return 0;
}