寒假集訓三補題與題解
A
分析
我們可以嘗試依次把每一隻小貓分配到一輛已經租用的纜車上,或者租用一輛纜車安置這種小貓
AC程式碼
#include<iostream> #include<algorithm> #define N 20 using namespace std; int n,m; int cat[N],sum[N],ans=N; bool cmp(int a,int b) { return a>b; } void dfs(int u,int k)//第u只貓,k輛車 { if(k>=ans) return ; if(u==n) { ans=k; return ; } for(int i=0;i<k;i++) if(sum[i]+cat[u]<=m) { sum[i]+=cat[u]; dfs(u+1,k); sum[i]-=cat[u]; } //再來輛車 sum[k]=cat[u]; dfs(u+1,k+1); sum[k]=0; } int main() { cin>>n>>m; for(int i=0;i<n;i++) cin>>cat[i]; sort(cat,cat+n,cmp); dfs(0,0); cout<<ans<<endl; return 0; }
B
分析
因為0到1的距離就是1到0的距離,比較暴力的想法是對每個1跑一下bfs,但是顯然會超時,多源BFS能很好的解決這個問題,因為如果我們把所有源點加入佇列後,跑出來的路徑距離依然是最短路,因為對於每個源點派生出來的分支,只在每一層上遍歷的順序不同,對於不同深度(即距離),也是遵循從上至下,所以跑出來的距離依然是最短路~。~
AC程式碼
#include<iostream> #include<algorithm> #include<cstring> #include<queue> using namespace std; typedef pair<int, int> PII; char a[1009][1009]; int g[1009][1009]; int n,m; queue<PII> p; void bfs() { //for(int i=0;i<n;i++) // for(int j=0;j<m;j++) // { // //cin>>a[i][j]; // if(a[i][j]=='1') // { p.push({i,j}); // g[i][j]=0;} // } int dx[4]={1,0,-1,0},dy[4]={0,-1,0,1}; while(p.size()) { PII t; t=p.front(); p.pop(); for(int i=0;i<4;i++) { int xix=t.first+dx[i],xiy=t.second+dy[i]; if(xix>=0&&xix<n&&xiy>=0&&xiy<m&&g[xix][xiy]==-1) { g[xix][xiy]=g[t.first][t.second]+1; p.push({xix,xiy}); } } } } int main() { memset(g, -1, sizeof g); cin>>n>>m; for(int i=0;i<n;i++) for(int j=0;j<m;j++) { cin>>a[i][j]; if(a[i][j]=='1') { p.push({i,j}); g[i][j]=0;} } bfs(); for(int i=0;i<n;i++) { for(int j=0;j<m;j++) cout<<g[i][j]<<" "; puts(""); } return 0; }
C
分析
搜尋順序:依次列舉每個字元對應哪個數字
剪枝:
1.從低位向高位依次考慮每一位:
a,b,c,t
被加數 加數 和 進位
(a+b+t) mod n=c
2.由於和也是n位數 ,因此最高位不可以有進位
3.從最低位開始列舉每個未知數
path[N]每個字母對應的數字
q[N] 從低位到高位字母出現的順序
st[N] 每個數字有沒有被用過
AC程式碼
#include<iostream> #include<cstring> #include<algorithm> using namespace std; const int N=30; int n; int path[N],q[N]; char e[3][N]; bool st[N]; bool check() { for(int i=n-1,t=0;i>=0;i--) { int a=e[0][i]-'A',b=e[1][i]-'A',c=e[2][i]-'A'; if(path[a]!=-1&&path[b]!=-1&&path[c]!=-1) { a=path[a];b=path[b];c=path[c]; if(t!=-1) { if((a+b+t)%n!=c) return false; if(!i&&a+b+t>=n) return false; t=(a+b+t)/n; } else { if((a+b)%n!=c&&(a+b+1)%n!=c) return false; if(!i&&a+b>=n) return false; } } else t=-1; } return true; } bool dfs(int u) { if(u==n) return true; for(int i=0;i<n;i++) if(!st[i]) { st[i]=true; path[q[u]]=i; if(check()&&dfs(u+1)) return true; st[i]=false; path[q[u]]=-1; } return false; } int main() { cin.tie(0); cout.tie(0); cin>>n; for(int i=0;i<3;i++) cin>>e[i]; for(int i=n-1,k=0;i>=0;i--) for(int j=0;j<3;j++) { int t=e[j][i]-'A'; if(!st[t]) { st[t]=true; q[k++]=t; } } memset(st,0,sizeof st); memset(path,-1,sizeof path); dfs(0); for(int i=0;i<n;i++) cout<<path[i]<<" "; return 0; }
F
分析
Floyd
1.本題的思路就是考慮最小環裡面節點編號最大的節點為k,且環裡面與k相連的兩個點為i,j,環的長度為g[i][k]+g[k][j]+d[j][i];
2.則d[j][i]則表示j到i且經過的節點編號小於k,因為在環中k就是最大的,只能經過小於k的節點了;
3.則這與floyd中k次迴圈開始前的d[i][j]意義相同;
4.那就不妨在floyd的第一重迴圈就求一下以k為最大節點編號的環的長度,注意這裡的k必須與節點的意義一樣:0-n-1或1-n;
AC程式碼
#include<iostream> #include<cstring> #include<algorithm> using namespace std; const int N=110,INF=0x3f3f3f; int n,m; int d[N][N],g[N][N]; int pos[N][N],path[N],cnt; void yoy(int a,int b) { if(pos[a][b]==0) return ; int c=pos[a][b]; yoy(a,c); path[cnt++]=c; yoy(c,b); } int main() { cin>>n>>m; memset(g,0x3f,sizeof g); for(int i=1;i<=n;i++) g[i][i]=0; while(m--) { int a,b,c; cin>>a>>b>>c; g[a][b]=g[b][a]=min(g[a][b],c); } int res=INF; memcpy(d,g,sizeof g); for(int k=1;k<=n;k++) { for(int i=1;i<k;i++) for(int j=i+1;j<k;j++) if((long long)d[i][j]+g[j][k]+g[k][i]<res) { res=d[i][j]+g[j][k]+g[k][i]; cnt=0; path[cnt++]=k; path[cnt++]=i; yoy(i,j); path[cnt++]=j; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(d[i][k]+d[k][j]-d[i][j]<0) { d[i][j]=d[i][k]+d[k][j]; pos[i][j]=k; } } if(res==INF) puts("No solution."); else { for(int i=0;i<cnt;i++) cout<<path[i]<<' '; puts(""); } return 0; }
H
分析
給定一組字母的大小關係,要你判斷是否在某一次讀入後,能夠判斷
1.該字母序列有序,並依次輸出;
2.該序列不能判斷是否有序;
3.該序列字母次序之間有矛盾,即有環存在。
而這三種形式的判斷應該遵循這樣的順序:先判斷是否有環(3),再判斷是否有序(1),最後才能判斷是否能得出結果(2)。
注意:對於(2)必須遍歷完整個圖!!,而(1)和(3)一旦得出結果,對後面的輸入就不用做處理了。
AC程式碼
#include<iostream> #include<vector> #include<algorithm> #include<queue> using namespace std; vector<int>G[30];//儲存圖 int in[30];//入度 char ans[30]; int in2[30];//複製上邊的度。因為中間會牽扯到度的更改 int i; int n,m; int topu() { bool logal=true;//表示是否是全排列 memcpy(in2,in,sizeof(in));//進行復制操作 queue<int>Q; int cnt=0;//進行數個數 for(int i=0;i<m;i++) { if(in[i]==0) Q.push(i); } while(!Q.empty()) { if(Q.size()>1) logal=false; int u=Q.front(); Q.pop(); ans[cnt++]=u+'A'; for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(--in2[v]==0)//寫錯了 Q.push(v); } } int result=0; if(cnt<m) result=-1;//說明存在環 else if(logal==true)//全序 result=1; return result; } int main() { string s[1030]; int flag; while(cin>>m>>n,n&&m) { memset(in,0,sizeof(in)); if(m==0&&n==0) break; for(i=0;i<m;i++) { G[i].clear(); } for( i=0;i<n;i++) { cin>>s[i]; } for(i=0;i<n;i++) { int u=s[i][0]-'A',v=s[i][2]-'A'; G[u].push_back(v); ++in[v]; if((flag=topu())!=0)//說明找到了一個全序,或者不滿足的條件 break; } ans[m]=0; // cout<<flag<<endl<<"*****"<<endl; if(flag==1) printf("Sorted sequence determined after %d relations: %s.\n",i+1,ans); else if(flag==0) printf("Sorted sequence cannot be determined.\n"); else if(flag==-1) printf("Inconsistency found after %d relations.\n",i+1); } return 0; }