什麼題目都不會做於是開始做搜索題。 然而我搜索題也不會做了。 鐵定沒戲的蒟蒻。 1.NOIP2004 蟲食算 “對於給定的N進位加法算式,求出N個不同的字母分別代表的數字,使得該加法算式成立。輸入數據保證有且僅有一組解”。 大概就是給你一堆(n個)字母讓你求出n進位下的一個n位數加n位數得到n位數的 ...
什麼題目都不會做於是開始做搜索題。
然而我搜索題也不會做了。
鐵定沒戲的蒟蒻。
1.NOIP2004 蟲食算
“對於給定的N進位加法算式,求出N個不同的字母分別代表的數字,使得該加法算式成立。輸入數據保證有且僅有一組解”。
大概就是給你一堆(n個)字母讓你求出n進位下的一個n位數加n位數得到n位數的唯一解(允許有前導0)。
千算萬算沒算到最大的優化是從大到小枚舉數字
反正頂多26位,個位開始爆搜2333。
幾個比較重要的剪枝:
當前列不可能滿足立即退出。
一列三個數,知二推一。
當前答案與之前衝突立即退出。
然後枚舉的時候從n枚舉到1。
過了。
可能寫得稍微那麼醜了一點?
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define rg register #define ls (x << 1) #define rs (x << 1 | 1) #define MID int mid=(l+r)>>1 using namespace std; const int N = 110; int fac[N],In[N],n,D[N]; char A[N],B[N],C[N]; int gi() { int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline char gc() { char ch=getchar(); while(ch>'Z' || ch<'A')ch=getchar(); return ch; } inline int F(rg char ch){return int(ch-'A'+1);} inline void Congratulations() { for(rg int i=1;i<=n;++i) printf("%d ",fac[i]); exit(0); } inline void dfs(rg int dep,rg int line) { if(line==1){ rg char ch=A[dep];rg int f=F(ch); if(fac[f]!=-1)dfs(dep,2); else for(rg int i=n-1;i>=0;--i) if(!In[i]){ fac[f]=i;In[i]=1; dfs(dep,2); fac[f]=-1;In[i]=0; } return; } else if(line==2){ rg char ch=B[dep];rg int f=F(ch); if(fac[f]!=-1)dfs(dep,3); else{ rg int f1=F(A[dep]),f3=F(C[dep]); rg int sum=fac[f1]+D[dep]; if(fac[f3]!=-1){ int x=fac[f3]-sum; while(x<0)x=x+n; if(!In[x]){ fac[f]=x;In[x]=1; dfs(dep,3); fac[f]=-1;In[x]=0; } } else for(rg int i=n-1;i>=0;--i) if(!In[i]){ fac[f]=i;In[i]=1; dfs(dep,3); fac[f]=-1;In[i]=0; } } return; } else{ rg char ch=C[dep];rg int f=F(ch); rg int sum=fac[F(A[dep])]+fac[F(B[dep])]+D[dep],ssum=sum-n; if(fac[f]!=-1){ if(fac[f]!=sum && fac[f]!=ssum)return; else{ if(sum>=n && dep==1)return; if(dep==1)Congratulations(); D[dep-1]=sum>=n;dfs(dep-1,1); } } else{ if(sum>=n){ if(dep==1)return; if(!In[ssum]){ D[dep-1]=1; fac[f]=ssum; In[ssum]=1; dfs(dep-1,1); In[ssum]=0; fac[f]=-1; } else return; } else{ if(!In[sum]){ if(dep==0)Congratulations(); D[dep-1]=0;fac[f]=sum;In[sum]=1; dfs(dep-1,1); In[sum]=0;fac[f]=-1; } else return; } } } } inline void work() { for(rg int i=1;i<=n;++i)fac[i]=-1; dfs(n,1); } int main() { n=gi(); for(int i=1;i<=n;++i)A[i]=gc(); for(int i=1;i<=n;++i)B[i]=gc(); for(int i=1;i<=n;++i)C[i]=gc(); work(); return 0; }
2.NOIP2009靶形數獨
一個數獨每個方格都有一定的權Ai,j,給你一個不完全的數獨,定義數獨分數為所有方格的(方格內數和該方格權的積)的和,求分數最大的數獨,無解輸出-1。
第一次打,直接從(1,1)枚舉到(9,9),爆搜2333。
90分。
一筍乾就來了興趣,然後(觀察了一下數據)發現答案集中在其他三個角的時候跑的賊吉八慢。
把角落上的點的個數劃分個區域統計一下,從那個點開始爆搜。
於是過了。
不科學啊,複雜度是應該很高的啊... ...
反正大力出奇跡嘛。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define ls (x << 1) #define rs (x << 1 | 1) #define MID int mid=(l+r)>>1 using namespace std; int map[21][21],bel[11][21][21],vis[21][21]; int In[21][21],In_1[21][21],In_2[21][21]; int point[]={0,6,7,8,9,10},Ans,f1,f2,f3,f4,fuc[21][21]; int gi() { int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline void TS() { for(int i=1;i<=9;++i){ for(int j=1;j<=9;++j) printf("%d ",vis[i][j]); printf("\n"); } } inline void dfs(int x,int y,int nf,int tc,int kind) { if(tc>=81){Ans=max(Ans,nf);return;} if(x<1 || y<1 || x>9 || y>9)return; if(map[x][y]){ vis[x][y]=map[x][y]; nf+=point[bel[2][x][y]]*map[x][y]; if(kind==1){ if(y==9)dfs(x+1,1,nf,tc+1,kind); else dfs(x,y+1,nf,tc+1,kind); } else if(kind==2){ if(y==1)dfs(x+1,9,nf,tc+1,kind); else dfs(x,y-1,nf,tc+1,kind); } else if(kind==3){ if(y==9)dfs(x-1,1,nf,tc+1,kind); else dfs(x,y+1,nf,tc+1,kind); } else if(kind==4){ if(y==1)dfs(x-1,9,nf,tc+1,kind); else dfs(x,y-1,nf,tc+1,kind); } return; } int Bel=bel[1][x][y],pt=point[bel[2][x][y]]; for(int i=9;i;--i){ if(!In[Bel][i]) if(!In_1[x][i]) if(!In_2[y][i]){ vis[x][y]=i; In[Bel][i]=1; In_1[x][i]=1; In_2[y][i]=1; int fn=nf+pt*i; if(kind==1){ if(y==9)dfs(x+1,1,fn,tc+1,kind); else dfs(x,y+1,fn,tc+1,kind); } else if(kind==2){ if(y==1)dfs(x+1,9,fn,tc+1,kind); else dfs(x,y-1,fn,tc+1,kind); } else if(kind==3){ if(y==9)dfs(x-1,1,fn,tc+1,kind); else dfs(x,y+1,fn,tc+1,kind); } else if(kind==4){ if(y==1)dfs(x-1,9,fn,tc+1,kind); else dfs(x,y-1,fn,tc+1,kind); } vis[x][y]=0; In[Bel][i]=0; In_1[x][i]=0; In_2[y][i]=0; } } } int main() { for(int i=1;i<=9;++i) for(int j=1;j<=9;++j) map[i][j]=gi(); for(int i=1;i<=9;++i) for(int j=1;j<=9;++j){ bel[1][i][j]=((i-1)/3)*3+((j-1)/3)+1; bel[2][i][j]=min(min(i,j),min(9-i+1,9-j+1)); } for(int i=1;i<=9;++i) for(int j=1;j<=9;++j) if(map[i][j]){ In[bel[1][i][j]][map[i][j]]=1; In_1[i][map[i][j]]=1; In_2[j][map[i][j]]=1; } for(int i=1;i<=9;++i) for(int j=1;j<=9;++j) if(map[i][j]){ if(i<=5 && j<=5)f1++; if(i<=5 && j>=5)f2++; if(i>=5 && j<=5)f3++; if(i>=5 && j>=5)f4++; } if(f1>=f2 && f1>=f3 && f1>=f4)dfs(1,1,0,0,1); else if(f2>=f1 && f2>=f3 && f2>=f4)dfs(1,9,0,0,2); else if(f3>=f1 && f3>=f2 && f3>=f4)dfs(9,1,0,0,3); else dfs(9,9,0,0,4); printf("%d\n",Ans?Ans:-1); return 0; }
3.NOIP2010引水入城
一個矩形地帶,每個格子有高度。
最上面一行可以建蓄水廠,中間的可以建輸水站。
水只能從高處流向低處(平地不行),只能從一個點流向有公共邊的點。
問最少要幾個蓄水廠才能使最後一行的城市都有水。無解輸出-1。
矩形最大是500×500的。
首先很容易想到只在第一行的比左右都高的點修就可以了啊。
然後把這些點爆搜一遍看它能走到的最左邊和最右邊。
容易證明一個點能走到的一定是一條連續的線段,如果不是就可以-1了。
然後就是很經典的線段覆蓋區間問題。
貪心和DP都可以。我也搞不清是什麼方法。
處理這些線段先按左端點排序,然後開一個隊列,從左往右掃一遍。
如果當前線段覆蓋了上一條線段的"有用區間"(即相對上上個線段外的區間),就把上一個區間從隊列里去掉。
直到去不掉了就把這條線段加在隊列里。
如果被上一條覆蓋了,直接跳過算了。
如果沒覆蓋,就把這個線段加進隊列,進行下一波計算。
把細節卡好就差不多了,也不是什麼很難的題。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define rg register using namespace std; const int N = 510; const int Inf = 1e6+7; int n,m,high[N][N],L[N][N],R[N][N],Left[N],Right[N]; int st,ed=1,que[N],vis[N][N]; int gx[]={0,1,-1,0,0},gy[]={0,0,0,1,-1}; inline int gi() { rg int x=0,res=1;rg char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline bool check(rg int x,rg int y,rg int h) { if(x<1 || y<1 || x>n || y>m)return false; return h>high[x][y]; } inline void dfs(rg int bel,rg int x,rg int y) { if(L[x][y]!=m+1 && R[x][y]!=-1){ Left[bel]=min(Left[bel],L[x][y]); Right[bel]=max(Right[bel],R[x][y]); return; } if(vis[x][y])return;vis[x][y]=1; for(int i=1;i<=4;++i) if(check(x+gx[i],y+gy[i],high[x][y])){ dfs(bel,x+gx[i],y+gy[i]); L[x][y]=min(L[x][y],L[x+gx[i]][y+gy[i]]); R[x][y]=max(R[x][y],R[x+gx[i]][y+gy[i]]); } if(x==n){ L[x][y]=min(y,L[x][y]!=m+1?L[x][y]:y); R[x][y]=max(y,R[x][y]!=0-1?R[x][y]:y); } Left[bel]=min(Left[bel]?Left[bel]:L[x][y],L[x][y]); Right[bel]=max(Right[bel]?Right[bel]:R[x][y],R[x][y]); return; } int main() { n=gi();m=gi(); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ L[i][j]=m+1;R[i][j]=-1; high[i][j]=gi(); } high[n][0]=high[n][m+1]=-Inf; for(int i=1;i<=m;++i) Left[i]=m+1,Right[i]=-1,dfs(i,1,i); for(int i=1;i<=m;++i) if(L[n][i]>R[n][i])st++; if(st){printf("0\n%d\n",st);return 0;} st=1;ed=0; for(int i=1,last=1;i<=m;){ if(high[1][i]<high[1][i+1]){++i;continue;} if(high[1][i]<high[1][i-1]){++i;continue;} if(Left[i]>Right[i]){++i;continue;} if(ed==0){ que[++ed]=i; last=1; ++i; continue; } if(Left[i]<=last && Right[i]>=Right[que[ed]]){ ed--; if(ed>1)last=Right[ed-1]+1; else last=1; continue; } else if(Left[i]>last){ if(Right[i]>Right[que[ed]]){ last=Right[que[ed]]+1; que[++ed]=i; } ++i; } } printf("1\n%d\n",ed); return 0; }
4.NOIP2013華容道
給你一個華容道圖,裡面的小格子都是1×1的,有些小格子可以移動,有一個空格。
每一次移動操作需要的時間為1,問把一個指定塊移到一個指定區域的最小時間。
Q次詢問。矩形最大是30*30,Q最大是500
大概花了個二十分鐘就寫了個能鬼到65的BFS。有人說優化一下狀態可以搞到80。
然後開始想正解,發現完全沒什麼戲。於是去%了一下,經過一番鄙視和提點之後開始了漫長的征途。
首先預處理出,從一個點的上下左右到上下左右,不經過這個點的最短路。
可以固定住這個點,視為不可移動狀態,然後處理。
然後把空格移到起始點,然後把起始點移到終點。
用SPFA維護,大概要開三維狀態,(x,y)和方向(上下左右)。
OI界的毒瘤一顆。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define ls (x << 1) #define rs (x << 1 | 1) #define MID int mid=(l+r)>>1 using namespace std; const int N = 31; struct Data{int x,y,from;}; struct Dot{int x,y;}; int n,m,Q,map[N][N],Ex,Ey,Sx,Sy,Tx,Ty; int Gx[]={0,0,1,0,-1},Gy[]={0,1,0,-1,0}; int far[N][N][5][5],vis[N][N][5],dis[N][N][5]; int gi() { int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline bool check(int x,int y) { if(x<1 || y<1 || x>n || y>m)return false; return map[x][y]; } inline int fc(int x) { if(x!=2)return (x+2)%4; return 4; } inline int BFS(int fx,int fy,int tx,int ty) { int Vis[N][N],dep[N][N]; memset(Vis,0,sizeof(Vis)); memset(dep,127,sizeof(dep)); queue<Dot>Q;Q.push((Dot){fx,fy}); Vis[fx][fy]=1;dep[fx][fy]=0; while(!Q.empty()){ Dot now=Q.front();Q.pop(); int x=now.x,y=now.y;Vis[x][y]=0; for(int i=1;i<=4;++i) if(check(x+Gx[i],y+Gy[i])) if(dep[x][y]+1<dep[x+Gx[i]][y+Gy[i]]){ int xx=x+Gx[i],yy=y+Gy[i]; dep[xx][yy]=dep[x][y]+1; if(!Vis[xx][yy]){ Vis[xx][yy]=1; Q.push((Dot){xx,yy}); } } } return dep[tx][ty]==dep[0][0]?far[0][0][0][0]:dep[tx][ty]; } inline void prepare() { memset(far,127,sizeof(far)); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(map[i][j]){ map[i][j]=0; for(int k=1;k<=4;++k) if(check(i+Gx[k],j+Gy[k])) for(int l=1;l<=4;++l) if(check(i+Gx[l],j+Gy[l])) if(k!=l) far[i][j][k][l]=BFS(i+Gx[k],j+Gy[k],i+Gx[l],j+Gy[l]); else far[i][j][k][l]=0; map[i][j]=1; } } inline void solve() { if(check(Ex,Ey) && check(Sx,Sy) && check(Tx,Ty)); else{printf("-1\n");return;} memset(vis,0,sizeof(vis)); memset(dis,127,sizeof(dis)); for(int i=1;i<=4;++i) vis[Sx][Sy][i]=1,dis[Sx][Sy][i]=0; queue<Data>Q;map[Sx][Sy]=0; for(int i=1;i<=4;++i) if(check(Sx+Gx[i],Sy+Gy[i])){ int d=BFS(Ex,Ey,Sx+Gx[i],Sy+Gy[i]); if(d<far[0][0][0][0]){ dis[Sx+Gx[i]][Sy+Gy[i]][fc(i)]=d+1; vis[Sx+Gx[i]][Sy+Gy[i]][fc(i)]=1; Q.push((Data){Sx+Gx[i],Sy+Gy[i],fc(i)}); } } map[Sx][Sy]=1; while(!Q.empty()){ Data now=Q.front();Q.pop(); int x=now.x,y=now.y,from=now.from; vis[x][y][from]=0; for(int i=1;i<=4;++i) if(check(x+Gx[i],y+Gy[i])){ int xx=x+Gx[i],yy=y+Gy[i]; int dt=far[x][y][from][i]; if(dis[x][y][from]+dt+1<dis[xx][yy][fc(i)]){ dis[xx][yy][fc(i)]=dis[x][y][from]+dt+1; if(!vis[xx][yy][fc(i)]){ Q.push((Data){xx,yy,fc(i)}); vis[xx][yy][fc(i)]=1; } } } } int ans=dis[0][0][0]; for(int i=1;i<=4;++i)ans=min(ans,dis[Tx][Ty][i]); if(ans==dis[0][0][0])printf("-1\n"); else printf("%d\n",ans); } int main() { n=gi();m=gi();Q=gi(); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) map[i][j]=gi(); prepare(); while(Q--){ Ex=gi();Ey=gi(); Sx=gi();Sy=gi(); Tx=gi();Ty=gi(); if(Sx==Tx && Sy==Ty){ printf("0\n"); continue; } solve(); } return 0; }
5.NOIP2015鬥地主
給你幾張牌,給你一些規則,問你這些牌最少幾步打完。牌數不超過23張。
一個剪枝:先把四帶、三帶等打完,粗略計算答案上界。
我也不知道為什麼要先帶掉單個的。
然後就是很簡單的爆搜2333了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #include <complex> #include <stack> #define LL long long int #define ls (x << 1) #define rs (x << 1 | 1) #define MID int mid=(l+r)>>1 using namespace std; int bin[21],Ans,n,T; int gi() { int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } inline bool check_UP() { for(int i=1;i<=14;++i) if(bin[i])return false; return true; } inline void dfs(int step) { if(step>=Ans)return; if(check_UP()){Ans=step;return;} int s1,s2,s3,s4;s1=s2=s3=s4=0; for(int i=1;i<=14;++i){ if(bin[i]==1)++s1; if(bin[i]==2)++s2; } for(int i=1;i<=13;++i) if(bin[i]==4){ s4++; if(s1>=2)s1-=2; else if(s2>=2)s2-=2; else s1--; } for(int i=1;i<=13;++i) if(bin[i]==3){ s3++; if(s1)s1--; else if(s2)s2--; } Ans=min(Ans,step+s1+s2+s3+s4); //三順子! for(int i=1;i<=11;++i) if(bin[i]>=3 && bin[i+1]>=3){ int j=i+2; bin[i]-=3;bin[i+1]-=3; dfs(step+1); while(bin[j]>=3 && j<=12)bin[j]-=3,dfs(step+1),++j; for(int k=i;k<j;++k)bin[k]+=3; } //Congratulations! //雙順子! for(int i=1;i<=10;++i) if(bin[i]>=2 && bin[i+1]>=2 && bin[i+2]>=2){ int j=i+3; bin[i]-=2;bin[i+1]-=2;bin[i+2]-=2; dfs(step+1); while(bin[j]>=2 && j<=12)bin[j]-=2,dfs(step+1),++j; for(int k=i;k<j;++k)bin[k]+=2; } //Congratulations! //單順子! for(int i=1;i<=8;++i) if(bin[i]>=1 && bin[i+1]>=1 && bin[i+2]>=1 && bin[i+3]>=1 && bin[i+4]>=1){ int j=i+5; bin[i]--;bin[i+1]--;bin[i+2]--;bin[i+3]--;bin[i+4]--; dfs(step+1); while(bin[j]>=1 && j<=12)bin[j]--,dfs(step+1),++j; for(int k=i;k<j;++k)bin[k]++; } //Congratulations! /* //四帶一/二! for(int i=1;i<=13;++i) if(bin[i]==4){ bin[i]-=4; for(int j=1;j<=14;++j) if(bin[j]>=2) for(int k=1;k<=14;++k) if(bin[k]>=2){ bin[j]-=2;bin[k]-=2; dfs(step+1); bin[j]+=2;bin[k]+=2; } for(int j=1;j<=14;++j) if(bin[j]>=1) for(int k=1;k<=14;++k) if(bin[k]>=1){ bin[j]-=1;bin[k]-=1; dfs(step+1); bin[j]+=1;bin[k]+=1; } bin[i]+=4; } //Congratulations! //三帶二! for(int i=1;i<=13;++i) if(bin[i]>=3){ bin[i]-=3; for(int j=1;j<=14;++j) if(bin[j]>=2){ bin[j]-=2; dfs(step+1); bin[j]+=2; } bin[i]+=3; } //Congratulations! //三帶一!(附炸彈) for(int i=1;i<=13;++i) if(bin[i]>=3){ bin[i]-=3; for(int j=1;j<=14;++j) if(bin[j]>=1){ bin[j]-=1; dfs(step+1); bin[j]+=1; } bin[i]+=3; } //Congratulations! //三張牌! for(int i=1;i<=13;++i) if(bin[i]>=3){bin[i]-=3;dfs(step+1);bin[i]+=3;} //Congratulations! //對子牌! for(int i=1;i<=14;++i) if(bin[i]>=2){bin[i]-=2;dfs(step+1);bin[i]+=2;} //Congratulations! //單張! for(int i=1;i<=14;++i) if(bin[i]==1){bin[i]-=1;dfs(step+1);bin[i]+=1;} //Congratulations! */ } int main() { scanf("%d%d",&T,&n); while(T--){ memset(bin,0,sizeof(bin));Ans=n; for(int i=1;i<=n;++i){ int x=gi();gi(); if(x==0)bin[14]++; else if(x==1 || x==2)bin[x+11]++; else bin[x-2]++; } dfs(0); printf("%d\n",Ans); } return 0; }