天梯赛赛前练习
天梯賽賽前練習
模擬/暴力
L2-018 多項式A除以B(模擬多項式除法)
注意特殊的輸出格式:零多項式是一個特殊多項式,對應輸出為0 0 0.0
輸出的系數保留小數點后1位,舍入后為0.0,則不輸出
意味著輸出的數的絕對值至少都要大于0.05
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; double c1[maxn],c2[maxn],ac[maxn]; int main(){int n;cin>>n;int maxa=0;for(int i=0;i<n;i++){int x;double y;cin>>x>>y;maxa=max(maxa,x);c1[x]=y;}int m;cin>>m;int maxb=0;for(int i=0;i<m;i++){int x;double y;cin>>x>>y;maxb=max(maxb,x);c2[x]=y;}if(maxa<maxb){printf("0 0 0.0\n");printf("%d",n);for(int i=maxa;i>=0;i--){if(fabs(c1[i])>=0.05){printf(" %d %.1lf",i,c1[i]);}}printf("\n");}else{int ans=0;while(maxa>=maxb){int e=maxa-maxb;double c=c1[maxa]/c2[maxb];//printf("%d %.1lf\n",e,c);ac[e]+=c;ans=max(ans,e);double tmp[maxn];for(int j=maxb;j>=0;j--){int ee=e+j;double cc=c*c2[j];tmp[ee]=cc;}int mmax=0;for(int j=maxa;j>=0;j--){if(fabs(c1[j]-tmp[j])<0.05){c1[j]=0.0;continue;}else{c1[j]-=tmp[j];mmax=max(mmax,j);}}maxa=mmax; }int cnt=0;int mmax=0;for(int i=ans;i>=0;i--){if(fabs(ac[i])>=0.05){cnt++;mmax=max(mmax,i);}}if(cnt==0){printf("0 0 0.0\n");}else{printf("%d",cnt);for(int i=mmax;i>=0;i--){if(fabs(ac[i])>=0.05){printf(" %d %.1lf",i,ac[i]);}}printf("\n");}cnt=0;for(int i=maxa;i>=0;i--){if(fabs(c1[i])>=0.05){cnt++;}}if(cnt==0){printf("0 0 0.0\n");}else{printf("%d",cnt);for(int i=maxa;i>=0;i--){if(fabs(c1[i])>=0.05){printf(" %d %.1lf",i,c1[i]);}}printf("\n");}} return 0; }L2-028 秀恩愛分得快 (25 分)
“我們把所有人從 0 到 N-1 編號。為了區分性別,我們用編號前的負號表示女性”
注意==“-0”==的情況,代表0為女性,所以不能簡單的使用int輸入,判斷正負。
注意時間復雜度!無用數據不要管,只看對最終結果有影響的數據!
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e4+10; vector<int>img[maxn]; double sum[maxn][maxn]; int vis[maxn][maxn]; vector<int>ans1,ans2; int xb[maxn]; int check(string s){int x;if(s[0]=='-'){x=stoi(s.substr(1));xb[x]=-1;}else x=stoi(s),xb[x]=1; return x; } int main(){int n,m;cin>>n>>m;//存照片、性別 //不是所有照片都有用,只有s和t出現的照片才有用for(int i=0;i<m;i++){int k;cin>>k;for(int j=0;j<k;j++){string s;int x;cin>>s;x=check(s); img[i].push_back(x);vis[i][x]=1;//表示x出現在第i張照片上}}int x,y;string s,t;cin>>s>>t;x=check(s);y=check(t);double mmax=0,mmay=0;for(int i=0;i<m;i++){int k=img[i].size();if(vis[i][x]){//x出現在第i張照片上for(int j=0;j<img[i].size();j++){int a=img[i][j];if(xb[x]!=xb[a]){sum[x][a]+=1.0/k;mmax=max(sum[x][a],mmax);}}}if(vis[i][y]){//y出現在第i張照片上for(int j=0;j<img[i].size();j++){int a=img[i][j];if(xb[y]!=xb[a]){sum[y][a]+=1.0/k;mmay=max(sum[y][a],mmay);}}}}if(sum[x][y]==mmax&&sum[y][x]==mmay) cout<<s<<" "<<t<<endl;else{for(int i=0;i<n;i++){if(xb[x]!=xb[i]&&sum[x][i]==mmax){cout<<s<<" ";if(i!=0) cout<<xb[i]*i<<endl;else{if(xb[i]==-1) cout<<"-0\n";else cout<<"0\n";}}}for(int i=0;i<n;i++){if(xb[y]!=xb[i]&&sum[y][i]==mmay){cout<<t<<" ";if(i!=0) cout<<xb[i]*i<<endl;else{if(xb[i]==-1) cout<<"-0\n";else cout<<"0\n";}}}}return 0; }L2-030 冰島人 (25 分)
讀題!讀題!讀題!
冰島人姓名組成:名+姓(姓為其父親的名+性別后綴)
普通人姓名組成:名+姓(姓只是單純的姓氏+性別后綴)
“五代以內無公共祖先”是指兩人的公共祖先(如果存在的話)必須比任何一方的曾祖父輩分高。
意味著:如果a和b的公共祖先是c,c是a的第六代祖先,但卻是b的第三代祖先,也屬于五代以內存在公共祖先!
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; map<string,int>mp;//將名字轉為數字 map<string,string>fa;//記錄父輩 int xb[maxn];//性別 int vis[maxn]; int bx[maxn],by[maxn];//第幾代 int check(string f,string s){//標記性別和其父輩int l=s.size();string name;int flag;if(s[l-1]=='m'||s[l-1]=='f'){//父輩未知name=s.substr(0,l-1);flag=(s[l-1]=='m')?1:-1;}else if(s[l-1]=='n'){name=s.substr(0,l-4);fa[f]=name;flag=1;}else{name=s.substr(0,l-7);flag=-1;fa[f]=name;}return flag; } int main(){int n;cin>>n;for(int i=1;i<=n;i++){string fir,sec;cin>>fir>>sec;mp[fir]=i;xb[i]=check(fir,sec);}int m;cin>>m;for(int i=0;i<m;i++){string m1,x1,m2,x2;cin>>m1>>x1>>m2>>x2;if(xb[mp[m1]]*xb[mp[m2]]>0) puts("Whatever");else if(mp[m1]==0||mp[m2]==0) puts("NA");else{memset(vis,0,sizeof(vis));memset(bx,0,sizeof(bx));memset(by,0,sizeof(by));vis[mp[m1]]=1;int cnt1=1,cnt2=1;while(fa[m1]!=""){x1=fa[m1];int fx=mp[x1];cnt1++;bx[fx]=cnt1;vis[fx]=1;m1=x1; }int flag=0;if(vis[mp[m2]]){//m2就是m1的祖先flag=1;}else{while(fa[m2]!=""){x2=fa[m2];int fx=mp[x2];cnt2++;by[fx]=cnt2;if(vis[fx]){//公共祖先if(by[fx]<5||bx[fx]<5){flag=1;break;}break;}vis[fx]=1;m2=x2;}}if(!flag) puts("Yes");else puts("No");}}return 0; }L2-034 口罩發放 (25 分)
1、身份證號唯一標識一個人
2、合法記錄:身份證號必須是18位的數字(不是18位或者出現其他字符都不可以!)
3、“如果提交時間相同,則按照在列表中出現的先后順序決定。”
sort的時候不能只比時間,還要比較出現順序!!!
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; struct peo{string name;string id;int pid;int s;int time; }p[maxn]; map<string,int>mp,vis; vector<peo>ans; bool cmp(peo a,peo b){if(a.time==b.time) return a.pid<b.pid;return a.time<b.time; } int main(){int d,P;cin>>d>>P;for(int i=1;i<=d;i++){int t,s;cin>>t>>s;int cnt=0;for(int j=0;j<t;j++){string a,b;int x,y,z;cin>>a>>b>>x;scanf("%d:%d",&y,&z);if(b.size()!=18) continue;//判斷是否合法int flag=0;for(int i=0;i<b.size();i++){if(b[i]<'0'||b[i]>'9'){flag=1;break;}}if(flag) continue;p[cnt].pid=cnt;//出現順序p[cnt].name=a;p[cnt].id=b;p[cnt].s=x;p[cnt].time=y*60+z;if(p[cnt].s==1&&vis[p[cnt].id]==0){ans.push_back(p[cnt]);vis[p[cnt].id]=1;}cnt++;}sort(p,p+cnt,cmp);int num=0;for(int j=0;j<cnt;j++){string id=p[j].id;if(num>=s) break;if(mp[id]==0||(i-mp[id]>P)){cout<<p[j].name<<" "<<p[j].id<<endl;mp[p[j].id]=i;//更新此人領口罩的snum++;}}}for(int i=0;i<ans.size();i++){cout<<ans[i].name<<" "<<ans[i].id<<endl;}return 0; }L1-002 打印沙漏 (20 分)
主要是根據等差數列求和公式來計算行數,不要直接計算(容易出錯)
這樣遍歷,不會出錯,而且數據也很小,不會T。
while(1){//首項從3開始: 2*r*(r+2)+1>n//首項從1開始,公差為2,利用等差數列求和公式na+n(n-1)/2:r*r-1>n,r--if(2*(r+1)*(r+1)-1>n){//首項從1開始,利用奇數項求和公式(n+1)(a+nd)break;}r++; }L1-006 連續因子 (20 分)
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main(){int n;cin>>n;int len=-1;int ans=2;for( l=2;l<=sqrt(n);l++){//如果寫成l*l<=n,則要開long long,所以最好還是寫成這樣if(n%l==0){int m=n;int r=l;while(m%r==0){m/=r;r++;}if(len<r-l){len=r-l;ans=l;}}}if(len==-1){cout<<"1\n";cout<<n<<endl;return 0;}cout<<len<<endl;for(int i=ans;i<ans+len;i++){if(i==ans) cout<<i;else cout<<"*"<<i;}return 0; }L1-009 N個數求和 (20 分)
注意審題!
輸入格式:
輸入第一行給出一個正整數N(≤100)。隨后一行按格式a1/b1 a2/b2 ...給出N個有理數。題目保證所有分子和分母都在長整型范圍內。另外,負數的符號一定出現在分子前面。
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b); } int main(){ll n;scanf("%lld",&n);ll fz,fm,g,lcm;scanf("%lld/%lld",&fz,&fm);for(int i=1;i<n;i++){ll x,y;scanf("%lld/%lld",&x,&y);g=gcd(y,fm);lcm=fm*y/g;fz=(lcm/fm)*fz+(lcm/y)*x; fm=lcm; }if(fz==0) cout<<"0\n";else{g=gcd(fz,fm);fz/=g;fm/=g;if(fm==1) cout<<fz<<endl;else if(fz/fm>0) cout<<fz/fm<<" "<<fz-(fz/fm)*fm<<"/"<<fm<<endl;else cout<<fz<<"/"<<fm<<endl;}return 0; }L3-006 迎風一刀斬 (30 分)
1、無論如何旋轉,形狀不會發生改變,由于只旋轉90、180或270,所以相鄰點要么橫坐標相等,要么縱坐標相等(除非該邊是斜邊)
2、一共4種切法
3+5 、 3+4 、 3+5 、4+4
4+4又可細分為兩種情況:
1)平行于坐標軸切開,兩矩形不存在斜邊,只需看是否有一邊邊長相等即可
2)兩梯形存在斜邊:需考慮一種特殊情況–>斜邊相等,直角腰不相等
#include<bits/stdc++.h> using namespace std; struct point{int x,y; }; int main(){int n;cin>>n;for(int i=0;i<n;i++){int k1,k2;cin>>k1;vector<point>v1,v2;for(int j=0;j<k1;j++){ //圖形輸入 int x,y;cin>>x>>y;v1.push_back({x,y});}cin>>k2;for(int j=0;j<k2;j++){int x,y;cin>>x>>y;v2.push_back({x,y});}if(k1+k2>8){ //非法情況 cout<<"NO\n";continue;}if(k1==4&&k2==4){ //特殊情況 int l=0,w=0,a=0,b=0; int cnt=0,z1=0,z2=0;for(int j=0;j<k1;j++){if(v1[j].x==v1[(j+1)%k1].x||(v1[j].y==v1[(j+1)%k1].y)) continue;l=abs(v1[j].x-v1[(j+1)%k1].x); //補全缺口,所需三角形的兩條直角邊長 w=abs(v1[j].y-v1[(j+1)%k1].y);//直角腰 z1=abs(v1[(j+2)%k1].x-v1[(j+3)%k1].x)+abs(v1[(j+2)%k1].y-v1[(j+3)%k1].y);}for(int j=0;j<k2;j++){if(v2[j].x==v2[(j+1)%k2].x||(v2[j].y==v2[(j+1)%k2].y)) continue; a=abs(v2[j].x-v2[(j+1)%k2].x);b=abs(v2[j].y-v2[(j+1)%k2].y);z2=abs(v2[(j+2)%k2].x-v2[(j+3)%k2].x)+abs(v2[(j+2)%k2].y-v2[(j+3)%k2].y);}if(a==l&&b==w||(a==w&&b==l)){ //兩梯形缺口互補 if(z1==z2) cout<<"YES\n"; //且直角腰相等 else cout<<"NO\n";}else cout<<"NO\n";continue;}vector<point>tmp;if(k1<k2){ //調整為 4-3、5-3 swap(k1,k2);tmp=v1;v1=v2;v2=tmp;}int l=0,w=0,a=0,b=0;for(int j=0;j<k1;j++){if(v1[j].x==v1[(j+1)%k1].x||(v1[j].y==v1[(j+1)%k1].y)) continue;l=abs(v1[j].x-v1[(j+1)%k1].x);w=abs(v1[j].y-v1[(j+1)%k1].y);}for(int j=0;j<k2;j++){if(v2[j].x==v2[(j+1)%k2].x){a=abs(v2[j].y-v2[(j+1)%k2].y);}else if(v2[j].y==v2[(j+1)%k2].y){b=abs(v2[j].x-v2[(j+1)%k2].x);}}if(a==l&&b==w||(a==w&&b==l)){ // 缺口和三角形可以匹配 cout<<"YES\n";}else cout<<"NO\n";}return 0; }字符串題
L1-078 吉老師的回歸 (15 分)
回想去年就是因為這道題有一個點一直過不了而痛失個人銅。。。
現在重新做題,也是發現了自己思維上的漏洞吧,果然還是自己實力有限。。。
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+10; typedef long long ll; string s[maxn]; int main() {int n,m;cin>>n>>m;getchar();for(int i=0;i<n;i++){getline(cin,s[i]);}int cnt=0;int i=0;for(;i<n;i++){int f1=s[i].find("qiandao");int f2=s[i].find("easy");//cout<<f1<<" "<<f2<<endl;沒找到返回-1if((f1!=-1)||(f2!=-1)) continue;else cnt++;if(cnt>m){//當前做題數為cnt+1,直接輸出cout<<s[i]<<endl;break;}}if(cnt<=m) cout<<"Wo AK le\n";//雖然做題數沒超過m,但是題目已經被全部做完了!AK!return 0; }L2-008 最長對稱子串 (25 分)
暴力
#include<bits/stdc++.h> using namespace std; int main(){string s;getline(cin,s);int l=0;int maxl=0;for(int i=0;i<s.size();i++){for(int j=s.size()-1;j>=i;j--){int l=i,r=j;while(l<=r&&s[l++]==s[r--]){if(l>r) maxl=max(maxl,j-i+1);}}}cout<<maxl<<endl;return 0; }WA
#include<bits/stdc++.h> using namespace std; int main(){string s;getline(cin,s);int l=0;int maxl=0;while(l<s.size()){int r=s.size()-1;while(s[r]!=s[l]&&r>l) r--;if(r<l){l++;continue;}int i=l;int j=r;while(i<=j&&s[i++]==s[j--]){if(i>j) maxl=max(maxl,r-l+1);}l++;}cout<<maxl<<endl;return 0; }STL
L2-039 清點代碼庫 (25 分)
map<vector,int>mp;
map默認按key值從小到大排序,因此vector會排好序
將map種的每對元素以pair的形式存入vector中,sort默認按pair的first從小到大進行排序
可將次數前加負號,也可重寫cmp函數
(不知道為什么不能用map<string,int>來寫,一直有兩個點過不去。。。)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e4+10; const int INF=0x3f3f3f3f; typedef pair<int,vector<int>> p; map<vector<int>,int>mp; vector<p>ans; //bool cmp(const p &a,const p &b){ // if(a.first!=b.first) return a.first>b.first; // for(int i=0;i<a.second.size();i++){ // if(a.second[i]!=b.second[i]){ // return a.second[i]<b.second[i]; // } // } //} int main(){int n,m;cin>>n>>m;for(int i=0;i<n;i++){vector<int>v;for(int j=0;j<m;j++){int x;cin>>x;v.push_back(x);}mp[v]++;}cout<<mp.size()<<endl;map<vector<int>,int>::iterator it1=mp.begin();for(;it1!=mp.end();it1++){ans.push_back(make_pair(-1*it1->second,it1->first));}sort(ans.begin(),ans.end());//sort(ans.begin(),ans.end(),cmp);vector<p>::iterator it2=ans.begin();for(;it2!=ans.end();it2++){cout<<-1*it2->first;for(int i=0;i<it2->second.size();i++){cout<<" "<<it2->second[i];}cout<<endl;}return 0; }L3-002 特殊堆棧 (30 分)
vector在插入和刪除的同時維護其升序狀態
vector插入和刪除時就按照大小順序進行,避免每次重新排序
vector的妙用
//vector添加指定位置的元素(下標從0開始) //在最前面的元素插入x v.insert(v.begin(),x); //在下標為2的元素前插入x v.insert(v.begin()+2,x); //在末尾插入x v.insert(v.end(),x);//vector刪除指定元素 //刪除下標為2的元素(第3個元素) v.erase(v.begin()+2); //刪除一段元素 v.erase(v.begin()+1,v.begin()+5);vector做法
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; vector<int>a; stack<int>st; int main(){int n;cin>>n;while(n--){string s;int x;cin>>s;if(s=="Pop"){if(st.empty()) puts("Invalid");else{int tmp=st.top();st.pop();int k=lower_bound(a.begin(),a.end(),tmp)-a.begin();a.erase(a.begin()+k);cout<<tmp<<endl;}}else if(s=="Push"){cin>>x;st.push(x);int k=lower_bound(a.begin(),a.end(),x)-a.begin();a.insert(a.begin()+k,x);}else{if(st.empty()) puts("Invalid");else{int l=a.size();int k=(l-1)/2;cout<<a[k]<<endl;}}}return 0; }線段樹做法
線段樹維護各正整數出現的次數,查找中值,即查詢線段樹上的第 ( c n t + 1 ) / 2 (cnt+1)/2 (cnt+1)/2個值
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; struct node{int l,r;int cnt;//區間[l,r]中各數字出現次數之和 }tree[maxn<<2]; int st[maxn]; void bt(int root,int l,int r){if(l==r){tree[root].l=tree[root].r=l;}else{tree[root].l=l;tree[root].r=r;int mid=(l+r)>>1;bt(root<<1,l,mid);bt(root<<1|1,mid+1,r);} } void update(int root,int idx,int val){//單點修改 if(tree[root].l==tree[root].r){tree[root].cnt+=val;}else{int mid=(tree[root].l+tree[root].r)>>1;if(idx<=mid) update(root<<1,idx,val);//注意區間判斷,無需進行無效遞歸else update(root<<1|1,idx,val);tree[root].cnt=tree[root<<1].cnt+tree[root<<1|1].cnt;//更新當前結點的值} } int query(int root,int num){//單點查詢 if(tree[root].l==tree[root].r){return tree[root].l;}if(tree[root<<1].cnt>=num) return query(root<<1,num); //左區間各數字出現次數就達到了num,那就只查詢左區間,否則在右區間中找剩下的else return query(root<<1|1,num-tree[root<<1].cnt); } int main(){bt(1,1,maxn);//建樹,維護結點表示的區間int n;cin>>n;int num=0;while(n--){string s;cin>>s;if(s=="Pop"){if(num==0) cout<<"Invalid\n";else{cout<<st[num]<<endl;update(1,st[num],-1);//出棧,將對應數字出現次數減1num--;}}else if(s=="Push"){int x;cin>>x;st[++num]=x;update(1,st[num],1);//入棧,將對應數字出現次數加1}else{if(num<=0) cout<<"Invalid\n";else{int mid=(num+1)>>1;cout<<query(1,mid)<<endl;//查詢,線段樹上的第mid個數字}}}return 0; }搜索
DFS
L3-029 還原文件(30分)
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+10; const int mod=1000000007; const int INF=0x3f3f3f3f; typedef long long ll; int n,m; int H[maxn],k[maxn]; int h[110][maxn]; vector<int>ans; int vis[110]; void dfs(int idx){if(ans.size()==m){for(int i=0;i<m;i++){cout<<ans[i];if(i==m-1) cout<<endl;else cout<<" ";}return;}for(int i=1;i<=m;i++){if(!vis[i]){int flag=1;for(int j=1;j<=k[i];j++){if(H[idx+j]!=h[i][j]){flag=0;break;}}if(flag){vis[i]=1;ans.push_back(i);dfs(idx+k[i]-1);vis[i]=0;ans.pop_back();}}} } int main(){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&H[i]);}scanf("%d",&m);for(int i=1;i<=m;i++){scanf("%d",&k[i]);for(int j=1;j<=k[i];j++){scanf("%d",&h[i][j]); }}dfs(0);return 0; }L2-016 愿天下有情人都是失散多年的兄妹
每行給出以下信息: 本人ID 性別 父親ID 母親ID本人性別明確,但其父母的性別未必明確(輸入中可能根本不存在其父母的信息),所以需要手動補充父母的性別信息!!!便于后續的判斷!
采集數據過程中注意數據的完整性!
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; const int INF=0x3f3f3f3f; typedef long long ll; vector<int>p[maxn]; int xb[maxn]; int vis[maxn]; int flag; void dfs(int x,int cnt){if(flag) return; if(cnt>5) return;vis[x]=1;for(int i=0;i<p[x].size();i++){if(!vis[p[x][i]]){dfs(p[x][i],cnt+1);}else{flag=1;break;}} } int main(){int n;cin>>n;for(int i=0;i<n;i++){int id,fa,mo;char s;cin>>id>>s>>fa>>mo;if(fa!=-1){p[id].push_back(fa);xb[fa]=1;//手動補充性別信息}if(mo!=-1){p[id].push_back(mo);xb[mo]=2;}if(s=='M') xb[id]=1;else xb[id]=2;} int k;cin>>k;for(int i=0;i<k;i++){int x,y;cin>>x>>y;if(xb[x]==xb[y]) puts("Never Mind");else{ memset(vis,0,sizeof(vis));flag=0;dfs(x,1);dfs(y,1);if(!flag) puts("Yes");else puts("No");}}return 0; }L3-001 湊零錢 (30 分)
DFS+剪枝
如果所有錢加在一起都不夠,那直接輸出"No Solution",否則最后一個測試點超時。
dfs里return的條件和剪枝條件注意先后順序!!!
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; int n,m; vector<int>money,ans; int flag; void dfs(int x,int sum){if(flag) return;if(sum==m){flag=1;for(int i=0;i<ans.size();i++){cout<<ans[i];if(i==ans.size()-1) cout<<endl;else cout<<" ";}return;}if(x==n||sum>m) return;//對于x=n的判斷不能放在sum=m的前面,有可能x=n時sum也恰好等于m,就會判錯ans.push_back(money[x]);dfs(x+1,sum+money[x]);ans.pop_back();dfs(x+1,sum); } int main(){cin>>n>>m;ll sum=0;for(int i=0;i<n;i++){int x;cin>>x;sum+=x;money.push_back(x);}if(sum<m) puts("No Solution");else{sort(money.begin(),money.end());dfs(0,0);if(!flag) puts("No Solution");}return 0; }01背包變形
恰好裝滿
//dp[i][j]:前i枚硬幣湊成不超過j元,最多選擇幾枚 //dp[i][j]=dp[i-1][j-a[i]]+1 #include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; int dp[maxn]; int a[maxn]; int pre[maxn]; int main(){int n,m;cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];}sort(a+1,a+n+1);memset(dp,-0x3f,sizeof(dp));//初始化為不可能取到的值dp[0]=0;dp[a[1]]=1;pre[a[1]]=a[1];for(int i=2;i<=n;i++){for(int j=m;j>=0;j--){if(j>=a[i]&&dp[j-a[i]]>=0){if(dp[j-a[i]]+1>=dp[j]){//注意取等,更新路徑dp[j]=dp[j-a[i]]+1;pre[j]=a[i];}}}}if(dp[m]<=0){cout<<"No Solution\n";}else{vector<int>ans;while(m>=a[1]){//cout<<pre[m]<<endl;ans.push_back(pre[m]);m-=pre[m];}for(int i=ans.size()-1;i>=0;i--){cout<<ans[i];if(i==0) cout<<endl;else cout<<" ";}}return 0; }L3-015 球隊“食物鏈” (30 分)
DFS+剪枝
輸入格式:
輸入第一行給出一個整數N(2≤N≤20),為參賽球隊數。隨后N行,每行N個字符,給出了N×N的聯賽結果表,其中第i行第j列的字符為球隊i在主場對陣球隊j的比賽結果:W表示球隊i戰勝球隊j,L表示球隊i負于球隊j*,D表示兩隊打平,-表示無效(當i=j時)。輸入中無多余空格。
輸出格式:
按題目要求找到“食物鏈”T1 T2 ? T**N,將這N個數依次輸出在一行上,數字間以1個空格分隔,行的首尾不得有多余空格。若不存在“食物鏈”,輸出“No Solution”。
1、剪枝優化:當剩余的所有球隊都未戰勝過第一支球隊時,無需繼續搜索。
2、注意題意:
L L L表示球隊 i i i負于球隊 j j j,即:球隊 j j j戰勝球隊 i i i
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=110; int n,flag; int vis[maxn],win[maxn][maxn]; vector<int>ans; void dfs(int x){if(flag) return;int f=0;for(int i=1;i<=n;i++){ //剪枝操作if(!vis[i]&&win[i][ans[0]]){f=1;break;}}if(!f) return;for(int i=1;i<=n;i++){if(!vis[i]&&win[x][i]){vis[i]=1;ans.push_back(i);if(ans.size()==n){if(win[i][ans[0]]){ //找到的第一個答案就是字典序最小的答案。flag=1;return;}}dfs(i);if(flag) return;//已經找到答案,直接returnvis[i]=0;ans.pop_back();}} } int main(){cin>>n;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){char ch;cin>>ch;if(ch=='W') win[i][j]=1;else if(ch=='L') win[j][i]=1;}}for(int i=1;i<=n;i++){ //vis無需每次清空,未找到答案,回溯到i時,之前被標記過的已經又全部被歸0了ans.push_back(i);flag=0;vis[i]=1;dfs(i);ans.pop_back();vis[i]=0;if(flag){for(int j=0;j<n;j++){if(j==0) cout<<ans[j];else cout<<" "<<ans[j];}cout<<endl;break;}}if(!flag) cout<<"No Solution\n";return 0; }BFS
L3-004 腫瘤診斷 (30 分)
三維BFS求連通塊的面積(連通數目)
注意審題!!!
六個方向搜索
#include<bits/stdc++.h> using namespace std; const int maxn=2e3+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; int m,n,l,t; int num,flag; int a[100][maxn][200]; int vis[100][maxn][200]; int dir[][6]={{0,-1,0},{0,0,1},{0,1,0},{0,0,-1},{1,0,0},{-1,0,0}}; struct node{int x,y,z; }; int bfs(int x,int y,int z){queue<node>q;q.push({x,y,z});int num=1;while(!q.empty()){node p=q.front();q.pop();for(int i=0;i<6;i++){int dx=p.x+dir[i][0];int dy=p.y+dir[i][1];int dz=p.z+dir[i][2];if(dx>=l||dx<0||dy>=m||dy<0||dz>=n||dz<0) continue;if(a[dx][dy][dz]==0||vis[dx][dy][dz]) continue;vis[dx][dy][dz]=1;num++;q.push({dx,dy,dz});}}return num; } int main(){cin>>m>>n>>l>>t;int ans=0;for(int i=0;i<l;i++){for(int j=0;j<m;j++){for(int k=0;k<n;k++){cin>>a[i][j][k];}}}for(int i=0;i<l;i++){for(int j=0;j<m;j++){for(int k=0;k<n;k++){if(vis[i][j][k]==0&&a[i][j][k]==1){vis[i][j][k]=1;int num=bfs(i,j,k);// cout<<num<<endl;if(num>=t) ans+=num;}}}}cout<<ans<<endl;return 0; }L3-008 喊山 (30 分)
第一想法DFS,但有兩個測試點一直過不去
參考題解,重新讀題,發現該題其實是分層的
如:
山頭1和山頭2臨近
山頭1和山頭3臨近
山頭2和山頭3臨近
其實對于山頭1來說,山頭2和山頭3的地位是一樣的(屬于同一層次),都是山頭1可以直接呼叫到的。
這里并不存在兩山頭間距離遠近的問題,dfs不適用
應該使用BFS,將各山頭“分層”
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; int n,m,k,q; vector<int>v[maxn]; int vis[maxn],lev[maxn]; int mmax; int ans; void bfs(int x){lev[x]=1;vis[x]=1;queue<int>q;q.push(x);while(!q.empty()){int y=q.front();q.pop();for(int i=0;i<v[y].size();i++){int z=v[y][i];if(vis[z]) continue;vis[z]=1;q.push(z);lev[z]=lev[y]+1;if(mmax<lev[z]){mmax=lev[z];ans=z;}else if(mmax==lev[z]){ans=min(ans,z);}}} } int main(){cin>>n>>m>>k;for(int i=0;i<m;i++){int x,y;cin>>x>>y;v[x].push_back(y);v[y].push_back(x); }for(int i=0;i<k;i++){cin>>q;ans=0;if(v[q].size()>0){memset(vis,0,sizeof(vis));mmax=0;ans=INF;vis[q]=1;bfs(q);}cout<<ans<<endl;}return 0; }L3-018 森森美圖 (30 分)
6 6
9 0 1 9 9 9
9 9 1 2 2 9
9 9 2 0 2 9
9 9 1 1 2 9
9 9 3 3 1 1
9 9 9 9 9 9
2 1 5 4
選擇圖像上的兩個像素點A和B,則連接這兩個點的直線就將圖像分為兩部分
- 圖像的左上角是坐標原點(0,0),我們假設所有像素按矩陣格式排列,其坐標均為非負整數(即橫軸向右為正,縱軸向下為正)。橫軸為x,縱軸為y
- 忽略正好位于連接A和B的直線(注意不是線段)上的像素點,即不認為這部分像素點在任何一個劃分部分上,因此曲線也不能經過這部分像素點。
- 曲線是八連通的(即任一像素點可與其周圍的8個像素連通),但為了計算準確,某像素連接對角相鄰的斜向像素時,得分額外增加兩個像素分數和的2倍減一。例如樣例中,經過坐標為(3,1)和(4,2)的兩個像素點的曲線,其得分應該是這兩個像素點的分數和(2+2),再加上額外的(2+2)乘以(2?1),即約為5.66。
題目種的曲線指的是從起點(像素點A到像素點B的曲線),這樣的曲線一共有兩條,直線AB的上方一條,直線AB的下方也有一條。曲線上的點,可由現在所在的點向八個方向擴散,只要最終能連接AB即可。但最終曲線上的所有點的分數和應該最小(最短路)。
利用叉乘判斷點在直線的哪一邊
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; int a[110][110]; double dis[110][110]; int n,m; int tag;//0:上半部分,1:下半部分 int dir[][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}}; struct Point{int x,y;bool operator !=(const Point &c) const{//creturn x!=c.x||y!=c.y;} }A,B; int cross(Point a,Point b,Point p){//叉乘,判斷點在直線的哪一邊,等于0則在直線上return (b.x-a.x)*(p.y-a.y)-(p.x-a.x)*(b.y-a.y); } void bfs(){memset(dis,0x7f,sizeof(dis));//double不能直接memset成0x3fqueue<Point>q;while(!q.empty()) q.pop();q.push(A);dis[A.x][A.y]=a[A.x][A.y];while(!q.empty()){Point p=q.front();q.pop();for(int i=0;i<8;i++){int dx=p.x+dir[i][0];int dy=p.y+dir[i][1];Point next={dx,dy};if(dx<1||dx>n||dy<1||dy>m) continue;int f=cross(A,B,{dx,dy});//包含起點和終點if(tag==0&&next!=A&&next!=B&&cross(A,B,next)<=0) continue;else if(tag&&next!=A&&next!=B&&cross(A,B,next)>=0) continue;double num;if(i<4) num=a[dx][dy]+dis[p.x][p.y];//上下左右方向else num=(a[dx][dy]+dis[p.x][p.y])+(a[dx][dy]+a[p.x][p.y])*(sqrt(2)-1);//斜方向if(dis[dx][dy]<=num) continue;dis[dx][dy]=num;if(dx==B.x&&dy==B.y){//已經達到終點,無需再將其入隊,否則陷入死循環continue;}q.push(next);}} } int main(){cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>a[i][j];}}cin>>A.y>>A.x>>B.y>>B.x;//注意坐標輸入順序A.y++,A.x++,B.y++,B.x++;double ans=0;tag=0;bfs();ans+=dis[B.x][B.y];//上半部分tag=1;bfs();ans+=dis[B.x][B.y];//下半部分printf("%.2lf\n",ans-a[A.x][A.y]-a[B.x][B.y]);//起點和終點算了兩次,需要減去return 0; }鏈表
L2-022 重排鏈表 (25 分)
坑點:輸入數據中可能會出現多余的無關結點(不在此鏈表上的結點、出現多個next為-1的結點)
最終鏈表結點個數,不一定就是題目所給的輸入 n n n
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; const int mod=1000000007; const int INF=0x3f3f3f3f; typedef long long ll; struct node{int add;int data;int next; }p[maxn]; vector<int>pre,las; int main(){int head,n;scanf("%d%d",&head,&n);for(int i=0;i<n;i++){int ad,x,nex;scanf("%d%d%d",&ad,&x,&nex);p[ad].add=ad;p[ad].data=x;p[ad].next=nex;}int q=head;int m=0;//鏈表實際結點個數while(p[q].next!=-1){m++;q=p[q].next;}q=head;int cnt=1;while(p[q].next!=-1){if(cnt<=m/2) las.push_back(p[q].add);else pre.push_back(p[q].add);cnt++;q=p[q].next;}pre.push_back(p[q].add);int l1=pre.size();int l2=las.size();int i=l1-1,j=0;while(i>=0&&j<l2){//cout<<"i: "<<i<<" j: "<<j<<endl;printf("%05d %d %05d\n",pre[i],p[pre[i]].data,las[j]);if(i==0) printf("%05d %d -1\n",las[j],p[las[j]].data);else printf("%05d %d %05d\n",las[j],p[las[j]].data,pre[i-1]);i--;j++;}if(i>=0) printf("%05d %d -1\n",pre[i],p[pre[i]].data);return 0; }數據結構
線段樹
L3-017 森森快遞 (30 分)
一條直線上的 N N N個城市,這些城市從左到右依次從0到 ( N ? 1 ) (N-1) (N?1)編號;第 i i i號城市( i = 0 , ? , N ? 2 i=0,\cdots ,N-2 i=0,?,N?2)與第 ( i + 1 ) (i+1) (i+1)號城市中間往返的運輸貨物重量在同一時刻不能超過 C i C_i Ci?公斤。
現有 Q Q Q張訂單,第 j j j張訂單描述了某些指定的貨物要從 S j S_j Sj?號城市運輸到 T j T_j Tj?號城市。所有貨物都有無限貨源,不定時的挑選一定的貨物進行運輸,且中途不能卸貨。
如何安排訂單的運輸,能使得運輸的貨物重量最大且符合道路的限制,求出運輸貨物重量的最大值。
即:若選擇訂單 j j j,則運輸的貨物不能超過從 S j S_j Sj?到 T j T_j Tj?途徑所有道路上運輸限制的最小值
線段樹維護區間最小值
由于運輸貨物的時間不定,任意時間都可以運輸,所以可能出現某一條道路同時運輸多個訂單貨物的情況。
如上圖所示,如果在某一時刻選擇從1到4運輸重量為1的貨物,則此時要選擇從1到3運輸貨物,運輸的貨物重量也只能是1
也就是說每選取一個區間,就要將其所有子區間(途徑的所有道路運輸貨物的重量)的值減去該區間的最小值(即修改區間最小值)。
而這就涉及到了區間修改的順序問題,由于我們想讓最終運輸的貨物重量盡可能地大,所以每次選擇了一個區間之后,應該對盡可能少地區間產生影響。
區間順序選擇:
當區間左端點相同,我們應優先選擇右端點小的區間(綠色的區間),不會對 [ R 1 , R 2 ] [R_1,R_2] [R1?,R2?]中的值產生影響。
>
當區間右端點相同,我們應優先選擇左端點大的區間(綠色的區間),不會對 [ L 1 , L 2 ] [L_1,L_2] [L1?,L2?]中的值產生影響
注意求最小值時的初值:1ll<<60
賦成0x3f3f3f3f最后一個測試點過不了!
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; const ll INF=1ll<<60; struct node{int l,r; }p[maxn]; bool cmp(node a,node b){//區間排序if(a.r==b.r) return a.l>b.l;return a.r<b.r; } struct Tree{int l,r;ll mix;ll lazy; }tr[maxn<<2]; void push_up(int root){tr[root].mix=min(tr[root<<1].mix,tr[root<<1|1].mix); } void bt(int root,int l,int r){tr[root].l=l;tr[root].r=r;tr[root].lazy=0;if(l==r){cin>>tr[root].mix;return;}int mid=(l+r)>>1;bt(root<<1,l,mid);bt(root<<1|1,mid+1,r);push_up(root); } void push_down(int root){if(tr[root].lazy){tr[root<<1].lazy+=tr[root].lazy;tr[root<<1].mix+=tr[root].lazy;tr[root<<1|1].lazy+=tr[root].lazy;tr[root<<1|1].mix+=tr[root].lazy;tr[root].lazy=0;} } void update(int root,int l,int r,int val){if(tr[root].l>r||tr[root].r<l) return;if(tr[root].l>=l&&tr[root].r<=r){tr[root].mix+=val;tr[root].lazy+=val; return;}push_down(root);int mid=(tr[root].l+tr[root].r)>>1;update(root<<1,l,r,val);update(root<<1|1,l,r,val);push_up(root); } ll query(int root,int l,ll r){if(tr[root].l>r||tr[root].r<l) return INF;if(tr[root].l>=l&&tr[root].r<=r){return tr[root].mix;}push_down(root);ll ans;int mid=(tr[root].l+tr[root].r)>>1;ans=min(query(root<<1,l,r),query(root<<1|1,l,r));return ans; } int main(){int n,q;cin>>n>>q;bt(1,1,n-1);for(int i=0;i<q;i++){int x,y;cin>>x>>y;if(x>y) swap(x,y);p[i].l=x;p[i].r=y;}sort(p,p+q,cmp);ll ans=0;for(int i=0;i<q;i++){int l=p[i].l;int r=p[i].r;ll num=query(1,l+1,r);//區間查詢ans+=num;if(num>0) update(1,l+1,r,-num);//區間修改}cout<<ans<<endl;return 0; }樹
1、按照題目所給定義建樹:左子樹鍵值大,右子樹鍵值小
一邊建樹,一邊維護層序
2、判斷是否是完全二叉樹
只需看層序遍歷每個結點的序號是否滿足從1到n,只要漏掉一個,都不是完全二叉樹。
L3-010 是否完全二叉搜索樹 (30 分)
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+10; int a[maxn],vis[maxn]; map<int,int>lev; void bt(int root,int val){//建樹if(val>lev[root]){ //將當前值插入適應結點int left=root<<1;while(1){if(lev[left]==0) {lev[left]=val;break;}if(val>lev[left]) left<<=1;else if(val<lev[left]) left=left<<1|1;}}else if(val<lev[root]){int right=root<<1|1;while(1){if(lev[right]==0){lev[right]=val;break;}if(val>lev[right]) right<<=1;else if(val<lev[right]) right=right<<1|1;}} } int main(){int n;cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}lev[1]=a[1];for(int i=2;i<=n;i++){bt(1,a[i]);}vector<int>idx;map<int,int>::iterator it=lev.begin();vis[it->first]=1;cout<<it->second;it++;for(;it!=lev.end();it++){vis[it->first]=1;cout<<" "<<it->second;}cout<<endl;int flag=0;for(int i=1;i<=n;i++){if(!vis[i]){flag=1;break;}}if(!flag) cout<<"YES\n";else cout<<"NO\n";return 0; }L3-016 二叉搜索樹的結構 (30 分)
map中是否出現過key:
mp.count(key);
若存在,返回1,否則,返回0;
雖然說,map用[ ]訪問不存在的key,value返回的是默認值(int為0,string為空串)
但是不知道為什么這道題,不能直接用mp[key]==0,來判,把這個改成count就對了。
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+10; int a[maxn]; map<int,int>lev,mp; void bt(int root,int val){//建樹if(lev.count(root)==0){//遇到空結點,則賦值lev[root]=val;mp[val]=root;return;}if(val<lev[root]) bt(root<<1,val); else if(val>lev[root]) bt(root<<1|1,val); } int main(){int n;cin>>n;for(int i=1;i<=n;i++){cin>>a[i];bt(1,a[i]);}int m;cin>>m;while(m--){int x,y;string a;cin>>x>>a; if(a=="is"){cin>>a>>a;if(a=="root"){if(mp[x]==1) cout<<"Yes\n";else cout<<"No\n";}else if(a=="parent"){cin>>a>>y;if(mp.count(x)==0||mp.count(y)==0) cout<<"No\n";else if(mp[y]/2==mp[x]) cout<<"Yes\n";else cout<<"No\n";}else if(a=="left"){cin>>a>>a>>y;if(mp.count(x)==0||mp.count(y)==0) cout<<"No\n";else if(mp[y]*2==mp[x]) cout<<"Yes\n";else cout<<"No\n";}else if(a=="right"){cin>>a>>a>>y;if(mp.count(x)==0||mp.count(y)==0) cout<<"No\n";else if(mp[y]*2+1==mp[x]) cout<<"Yes\n";else cout<<"No\n";} }else if(a=="and"){cin>>y>>a>>a;if(a=="siblings"){if(mp.count(x)==0||mp.count(y)==0) cout<<"No\n";else if(abs(mp[x]-mp[y])==1) cout<<"Yes\n";else cout<<"No\n";}else if(a=="on"){cin>>a>>a>>a;if(mp.count(x)==0||mp.count(y)==0) cout<<"No\n";else{int c=mp[x],d=mp[y];while(c!=0&&d!=0) c/=2,d/=2;if(c==0&&d==0)cout<<"Yes\n";else cout<<"No\n";} }}}return 0; }堆的建立
L2-012 關于堆的判斷 (25 分) 小頂堆
對輸入字符串的巧妙處理–>輸入格式已知
向上調整建堆
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; const int INF=0x3f3f3f3f; typedef long long ll; map<int,int>mp; int h[maxn]; int cnt; void up(int x){h[++cnt]=x;int t=cnt;while(t>1&&h[t/2]>h[t]){swap(h[t/2],h[t]);t/=2;//t>>=1}ans[t]=x; } int main(){int n,m;cin>>n>>m;for(int i=1;i<=n;i++){int x;cin>>x;up(x);}for(int i=1;i<=n;i++){mp[h[i]]=i;}getchar();for(int i=0;i<m;i++){int x,y;string s;cin>>x>>s;if(s=="and"){cin>>y>>s>>s;if(mp[x]/2==mp[y]/2) puts("T");else puts("F");}else if(s=="is"){cin>>s>>s;if(s=="root"){if(mp[x]==1) puts("T");else puts("F");}else if(s=="parent"){cin>>s>>y;if(mp[y]/2==mp[x]) puts("T");else puts("F");}else if(s=="child"){cin>>s>>y;if(mp[x]/2==mp[y]) puts("T");else puts("F");}} }return 0; }堆的基本操作
push_heap( )函數
make_heap( ):生成一個堆,大頂堆或小頂堆
push_heap( ): 向堆中插入一個元素,并且使堆的規則依然成立
[點擊參考此文章](make_heap()等函數的用法 - 陽離子 - 博客園 (cnblogs.com))
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; const int INF=0x3f3f3f3f; typedef long long ll; map<int,int>mp; int h[maxn]; int main(){int n,m;cin>>n>>m;for(int i=1;i<=n;i++){cin>>h[i];push_heap(h + 1, h + 1 + i, greater<int>());}for(int i=1;i<=n;i++){mp[h[i]]=i;}getchar();for(int i=0;i<m;i++){int x,y;string s;cin>>x>>s;if(s=="and"){cin>>y>>s>>s;if(mp[x]/2==mp[y]/2) puts("T");else puts("F");}else if(s=="is"){cin>>s>>s;if(s=="root"){if(mp[x]==1) puts("T");else puts("F");}else if(s=="parent"){cin>>s>>y;if(mp[y]/2==mp[x]) puts("T");else puts("F");}else if(s=="child"){cin>>s>>y;if(mp[x]/2==mp[y]) puts("T");else puts("F");}} }return 0; }并查集
L3-003 社交集群 (30 分)
具有同一興趣愛好的人歸為一類,最終可歸為幾類,每類各有多少人。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; vector<int>p[maxn]; int fa[maxn]; int num[maxn]; set<int>h; int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]); } bool cmp(int a,int b){return a>b; } int main(){int n;scanf("%d",&n);for(int i=1;i<=n;i++){int k;scanf("%d:",&k);for(int j=0;j<k;j++){int x;scanf("%d",&x);h.insert(x);//記錄所有興趣愛好(去重)p[x].push_back(i);//具有這一興趣愛好的人有哪些}}for(int i=1;i<=n;i++){fa[i]=i;num[i]=1;}set<int>::iterator it=h.begin();for(;it!=h.end();it++){int i=*it;int x=p[i][0];//將具有同一興趣愛好的人加入同一集合中for(int j=1;j<p[i].size();j++){int y=p[i][j];int fx=find(x);int fy=find(y);if(fx!=fy){fa[fx]=fy;num[fy]+=num[fx];}x=y; }}vector<int>ans;for(int i=1;i<=n;i++){if(find(i)==i){//記錄每個jans.push_back(num[i]);}}cout<<ans.size()<<endl;sort(ans.begin(),ans.end(),cmp);for(int i=0;i<ans.size();i++){if(i==0) cout<<ans[i];else cout<<" "<<ans[i];}return 0; }圖
L2-013 紅色警報 (25 分)—連通塊個數
DFS做法
被攻占的城市,不會再參與后續dfs找連通塊中,所以不能只用一個vis來標記(vis每一次都會被全部清空),需要再借助另外一個標記!
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+10; typedef long long ll; vector<int>v[maxn]; int vis[maxn];//每次dfs時,標記是否已被訪問過 int lost[maxn];//標記某城市是否已被攻占 int n,m; vector<int>ans; void dfs(int x){vis[x]=1;if(x>=n) return;for(int i=0;i<v[x].size();i++){if(vis[v[x][i]]==0&&(lost[v[x][i]]==0)) dfs(v[x][i]);} } int main() {cin>>n>>m;while(m--){int a,b;cin>>a>>b;v[a].push_back(b);v[b].push_back(a);}int cnt=0;//計算連通塊個數for(int i=0;i<n;i++){if(!vis[i]){dfs(i);cnt++;}}int k;cin>>k;for(int i=0;i<k;i++){int x;cin>>x;lost[x]=1;memset(vis,0,sizeof(vis));vis[x]=1;int num=0;for(int i=0;i<n;i++){if(!lost[i]&&(!vis[i])){dfs(i);num++;}}//cout<<"num: "<<num<<" cnt: "<<cnt<<endl;if(num>cnt) printf("Red Alert: City %d is lost!\n",x);else{printf("City %d is lost.\n",x);} cnt=num;if(i==n-1){cout<<"Game Over.\n";}}return 0; }并查集做法
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+10; typedef long long ll; int n,m; int fa[maxn]; int lost[maxn]; vector<pair<int,int> >road; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]); } int count(){for(int i=0;i<n;i++) fa[i]=i;for(int i=0;i<road.size();i++){int x=road[i].first;int y=road[i].second;if(!lost[x]&&(!lost[y])){int fx=find(x);int fy=find(y);fa[fx]=fy;}}int ans=0;for(int i=0;i<n;i++){if(find(i)==i) ans++;}return ans; } int main() {cin>>n>>m;for(int i=0;i<n;i++){fa[i]=i;}for(int i=0;i<m;i++){int x,y;cin>>x>>y;road.push_back({x,y});}int cnt=count();int k;cin>>k;for(int i=0;i<k;i++){int x;cin>>x;lost[x]=1;int num=count();//cout<<"num: "<<num<<" cnt: "<<cnt<<endl;//算連通塊時,會把被攻占的那個城市也算上,所以num要減一 if(num-1>cnt) printf("Red Alert: City %d is lost!\n",x);else printf("City %d is lost.\n",x);cnt=num;if(i==n-1) puts("Game Over.");}return 0; }最短路問題
L2-001 緊急救援 (25 分) 單源最短路
Dij做法
#include<bits/stdc++.h> using namespace std; const int maxn=1e3+10; const int INF=0x3f3f3f3f; typedef long long ll; int n,m,s,d; int c[maxn];//cure int e[maxn][maxn];//edge int dis[maxn];//shortest int vis[maxn];//visited int cis[maxn];//max_num int num[maxn];//the number of the shortest path vector<int>pre[maxn];//path vector<int>ans; void dfs(int x){ans.push_back(x);for(int i=0;i<pre[x].size();i++){dfs(pre[x][i]);} } void dij(){for(int i=0;i<n;i++){dis[i]=w[s][i];}cis[s]=c[s];num[s]=1;for(int i=0;i<n;i++){int mmin=INF,u=-1;for(int j=0;j<n;j++){if(!vis[j]&&dis[j]<mmin){mmin=dis[j];u=j;}}if(u==-1) break;vis[u]=1;for(int j=0;j<n;j++){if(!vis[j]&&dis[u]+e[u][j]<dis[j]){dis[j]=dis[u]+e[u][j];cis[j]=cis[u]+c[j];num[j]=num[u];pre[j].clear();pre[j].push_back(u);}else if(!vis[j]&&dis[u]+e[u][j]==dis[j]){num[j]+=num[u];if(cis[u]+c[j]>cis[j]){cis[j]=cis[u]+c[j];pre[j].clear();pre[j].push_back(u);}}}}cout<<num[d]<<" "<<cis[d]<<endl<<s;dfs(d);for(int i=ans.size()-2;i>=0;i--){cout<<" "<<ans[i];}} int main(){cin>>n>>m>>s>>d;for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(i==j) e[i][j]=0;else e[i][j]=INF;}}for(int i=0;i<n;i++){cin>>c[i];}for(int i=0;i<m;i++){int u,v,h;cin>>u>>v>>h;if(h<e[u][v]){e[u][v]=e[v][u]=h;}}dij();return 0; }優先隊列優化dij做法
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; typedef pair<int,int> P; struct edge{int to,w; }; vector<edge>e[maxn]; int c[maxn]; int dis[maxn]; int cis[maxn]; int vis[maxn]; int num[maxn]; vector<int>pre[maxn],ans; int n,m,s,d; void dfs(int x){ans.push_back(x);for(int i=0;i<pre[x].size();i++){dfs(pre[x][i]);} } void dij(){priority_queue<P,vector<P>,greater<P> >q;memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));dis[s]=0;num[s]=1;cis[s]=c[s];q.push(P(0,s));while(!q.empty()){P a=q.top();q.pop();int u=a.second;if(vis[u]) continue;vis[u]=1;for(int i=0;i<e[u].size();i++){edge x=e[u][i];if(!vis[x.to]&&dis[x.to]>dis[u]+x.w){dis[x.to]=dis[u]+x.w;cis[x.to]=cis[u]+c[x.to];num[x.to]=num[u];q.push(P(dis[x.to],x.to));pre[x.to].clear();pre[x.to].push_back(u);}else if(!vis[x.to]&&dis[x.to]==dis[u]+x.w){num[x.to]+=num[u];if(cis[x.to]<cis[u]+c[x.to]){cis[x.to]=cis[u]+c[x.to];// q.push(P(dis[x.to],x.to));pre[x.to].clear();pre[x.to].push_back(u);}} }}cout<<num[d]<<" "<<cis[d]<<endl;cout<<s;dfs(d);for(int i=ans.size()-2;i>=0;i--){cout<<" "<<ans[i];}cout<<endl;} int main(){cin>>n>>m>>s>>d;for(int i=0;i<n;i++){cin>>c[i];}for(int i=0;i<m;i++){int u,v,h;cin>>u>>v>>h;//無向邊!!!e[u].push_back({v,h});e[v].push_back({u,h});}dij();return 0; }L3-005 垃圾箱分布 (30 分)
審清題意!!!
注意最值的初始化!!!
所以垃圾箱的位置必須選在到所有居民點的最短距離最長的地方,同時還要保證每個居民點都在距離它一個不太遠的范圍內(距離不大于D)。
如果解不唯一,則輸出到所有居民點的平均距離最短的那個解。如果這樣的解還是不唯一,則輸出編號最小的地點。
因為垃圾箱最多只有十個,所以從每個垃圾箱出發,做dij
#include<bits/stdc++.h> using namespace std; const int maxn=1e4+10; #define INF 0x3f3f3f3f typedef pair<int,int> P; int dis[maxn]; int e[maxn][maxn]; int vis[maxn]; int n,m,K,D,s; map<int,string>mp; int ans,des,ans_sum; void dij(){priority_queue<P,vector<P>,greater<P> >q; memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));dis[s]=0;q.push(P(0,s));while(!q.empty()){P a=q.top();q.pop();int u=a.second;if(vis[u]) continue;vis[u]=1;for(int i=1;i<=n+m;i++){if(!vis[i]&&dis[i]>dis[u]+e[u][i]){dis[i]=dis[u]+e[u][i];q.push(P(dis[i],i));}} }int mmin=INF;int flag=1;int sum=0;for(int i=1;i<=n;i++){if(dis[i]==INF||dis[i]>D){flag=0;break;}sum+=dis[i];mmin=min(mmin,dis[i]);}if(flag){if(ans<mmin){ans=mmin;des=s;}if(ans==mmin){//最短距離最長的位置不唯一,則選平均距離最小的if(ans_sum>sum){des=s;ans_sum=sum;}else if(ans_sum==sum&&des>s){//平均距離最小的不唯一,則選序號小的des=s;}}}} int main() {cin>>n>>m>>K>>D;des=n+m+1;ans_sum=INF;for(int i=1;i<=n+m;i++){for(int j=1;j<=n+m;j++){if(i==j) e[i][j]=0;else e[i][j]=INF;}}while(K--){string a,b;int u,v,w;cin>>a>>b>>w;if(a[0]=='G'){u=n+a[1]-'0';mp[u]=a;}else u=stoi(a);if(b[0]=='G'){v=n+b[1]-'0';mp[v]=b;}else v=stoi(b);if(w<e[u][v]){e[u][v]=w;e[v][u]=w;}}for(int i=1;i<=m;i++){s=n+i;dij();} if(des==n+m+1){puts("No Solution");return 0;}cout<<mp[des]<<endl;s=des;dij();int sum=0;for(int i=1;i<=n;i++){sum+=dis[i];}printf("%.1lf %.1lf\n",1.0*ans,round(1.0*sum/n*10)/10);return 0; }L3-007 天梯地圖 (30 分)
分別針對最短時間和最短距離進行dij
注意輸出:
首先按下列格式輸出最快到達的時間T和用節點編號表示的路線:
Time = T: 起點 => 節點1 => ... => 終點然后在下一行按下列格式輸出最短距離D和用節點編號表示的路線:
Distance = D: 起點 => 節點1 => ... => 終點如果最快到達路線不唯一,則輸出幾條最快路線中最短的那條,題目保證這條路線是唯一的。
而如果最短距離的路線不唯一,則輸出途徑節點數最少的那條,題目保證這條路線是唯一的。
如果這兩條路線是完全一樣的,則按下列格式輸出:
Time = T; Distance = D: 起點 => 節點1 => ... => 終點在求最快到達路線時,不能直接用之前求得的最短路來更新路徑,因為最短路是按途徑節點數最少來尋找最優解的,而求最快到達路線時的最短的那條并不一定是節點數最少的那條
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; typedef pair<int,int>P; int n,m,s,t; int dis[maxn]; int tis[maxn]; int vis[maxn]; int num[maxn]; struct edge{int to;int time;int len; }; vector<edge>e[maxn]; vector<int>pre[maxn],tre[maxn],ans1,ans2; map<vector<int>,int>mp; void dfs1(int x){ans1.push_back(x);for(int i=0;i<tre[x].size();i++){dfs1(tre[x][i]);} } void dfs2(int x){ans2.push_back(x);for(int i=0;i<pre[x].size();i++){dfs2(pre[x][i]);} } void dij(){priority_queue<P,vector<P>,greater<P> >p,q;memset(dis,0x3f,sizeof(dis));memset(tis,0x3f,sizeof(tis));dis[s]=0;p.push(P(0,s));num[s]=1;while(!p.empty()){P a=p.top();p.pop();int u=a.second;if(vis[u]) continue;vis[u]=1;for(int i=0;i<e[u].size();i++){edge x=e[u][i];if(!vis[x.to]&&dis[x.to]>dis[u]+x.len){dis[x.to]=dis[u]+x.len;num[x.to]=num[u]+1;p.push(P(dis[x.to],x.to));pre[x.to].clear();pre[x.to].push_back(u);}if(!vis[x.to]&&dis[x.to]==dis[u]+x.len){if(num[x.to]>num[u]+1){//選擇節點數少的那條路pre[x.to].clear();pre[x.to].push_back(u);}} }}memset(vis,0,sizeof(vis));int d[550];tis[s]=0;q.push(P(0,s));while(!q.empty()){P a=q.top();q.pop();int u=a.second;if(vis[u]) continue;vis[u]=1;for(int i=0;i<e[u].size();i++){edge x=e[u][i];if(!vis[x.to]&&tis[x.to]>tis[u]+x.time){tis[x.to]=tis[u]+x.time;d[x.to]=d[u]+x.len;q.push(P(tis[x.to],x.to));tre[x.to].clear();tre[x.to].push_back(u);}else if(!vis[x.to]&&tis[x.to]==tis[u]+x.time){if(d[x.to]>d[u]+x.len){//選擇距離短的那條路(不能直接用前面的dis[])d[x.to]=d[u]+x.len;tre[x.to].clear();tre[x.to].push_back(u);}}}}dfs1(t);mp[ans1]=1;dfs2(t);if(mp[ans2]){//判斷路徑是否完全一樣cout<<"Time = "<<tis[t];cout<<"; ";}else{cout<<"Time = "<<tis[t]<<": "<<s;for(int i=ans1.size()-2;i>=0;i--){cout<<" => "<<ans1[i];}cout<<endl; }cout<<"Distance = "<<dis[t]<<": "<<s;for(int i=ans2.size()-2;i>=0;i--){cout<<" => "<<ans2[i];} } int main(){cin>>n>>m;for(int i=0;i<m;i++){int u,v,o,l,tt;cin>>u>>v>>o>>l>>tt;if(o){e[u].push_back({v,tt,l});}else{e[u].push_back({v,tt,l});e[v].push_back({u,tt,l});}}cin>>s>>t;dij();return 0; }L3-011 直搗黃龍 (30 分)
選擇合適的路徑,以最快的速度占領敵方大本營。(最短路)
當這樣的路徑不唯一時,要求選擇可以沿途解放最多城鎮的路徑。(經過節點最多)
若這樣的路徑也不唯一,則選擇可以有效殺傷最多敵軍的路徑。(殺傷敵軍最多)
注意路徑不唯一時,條件的判斷以及其他量的更新!!!
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int INF=0x3f3f3f3f; typedef long long ll; typedef pair<int,int> P; const int mod=1e9+7; map<string,int>mp; map<int,string>pm; int n,k,des; string s,t; int dis[maxn]; int vis[maxn]; int c[maxn]; int cis[maxn],num[maxn],sum[maxn]; struct node{int to;int w; }; vector<node>v[maxn]; vector<int>pre[maxn],ans; void dfs(int x){//輸出路徑ans.push_back(x);for(int i=0;i<pre[x].size();i++){dfs(pre[x][i]);} } void dij(){priority_queue<P,vector<P>,greater<P> >q;memset(dis,INF,sizeof(dis));dis[0]=0;q.push(P(0,0));cis[0]=0;num[0]=1;sum[0]=0;while(!q.empty()){P a=q.top();q.pop();int u=a.second;if(vis[u]) continue;vis[u]=1;for(int i=0;i<v[u].size();i++){node x=v[u][i];if(dis[x.to]>dis[u]+x.w){//最短路dis[x.to]=dis[u]+x.w;cis[x.to]=cis[u]+c[x.to];num[x.to]=num[u];sum[x.to]=sum[u]+1;pre[x.to].clear();pre[x.to].push_back(u);q.push(P(dis[x.to],x.to));}else if(dis[x.to]==dis[u]+x.w){num[x.to]+=num[u];//更新最多路的數量if(sum[x.to]<sum[u]+1){//經過節點數最多sum[x.to]=sum[u]+1;cis[x.to]=cis[u]+c[x.to];//不要忘記更新其他量pre[x.to].clear();pre[x.to].push_back(u); }else if(sum[x.to]==sum[u]+1&&cis[x.to]<cis[u]+c[x.to]){//殺傷敵軍最多cis[x.to]=cis[u]+c[x.to];pre[x.to].clear();pre[x.to].push_back(u);}} }}dfs(des);cout<<s;for(int i=ans.size()-2;i>=0;i--){cout<<"->"<<pm[ans[i]];}cout<<endl;cout<<num[des]<<" "<<dis[des]<<" "<<cis[des]<<endl; } int main(){cin>>n>>k>>s>>t;mp[s]=0;int cnt=1;for(int i=1;i<n;i++){string name;int x;cin>>name>>x;if(!mp[name]){mp[name]=cnt++;}pm[mp[name]]=name;c[mp[name]]=x;}for(int i=0;i<k;i++){string a,b;int w;cin>>a>>b>>w;v[mp[a]].push_back({mp[b],w});v[mp[b]].push_back({mp[a],w});}des=mp[t];dij();return 0; }計算幾何
L3-021 神壇 (30 分)
從n個點中任選3個點,求其所形成的三角形面積的最小值。
1、利用叉乘計算三角形面積: S = 1 2 ∣ A B ? × A C ? ∣ S=\frac{1}{2}|\vec{AB}\times\vec{AC}| S=21?∣AB×AC∣
2、級角排序
枚舉點,該點可與其他所有點連邊(形成向量),然后對所有向量按級角進行排序(順時針或逆時針排序);排好序之后,只有相鄰的兩個向量形成的三角形面積才可能是最小的。
參考:級角排序的方法:
1、利用atan2()函數按極角從小到大排序
bool cmp1(point a,point b) {if(atan2(a.y,a.x)!=atan2(b.y,b.x))return atan2(a.y,a.x)<atan2(b.y,b.x);else return a.x<b.x; }2、利用叉積按極角從小到大排序
bool cmp2(point a,point b) {point c;//原點c.x = 0;c.y = 0;if(compare(c,a,b)==0)//計算叉積,如果叉積相等,按照X從小到大排序return a.x<b.x;else return compare(c,a,b)>0; }3、先按象限從小到大排序 再按極角從小到大排序
int Quadrant(point a) //象限排序,注意包含四個坐標軸 {if(a.x>0&&a.y>=0) return 1;if(a.x<=0&&a.y>0) return 2;if(a.x<0&&a.y<=0) return 3;if(a.x>=0&&a.y<0) return 4; } bool cmp3(point a,point b) //先按象限從小到大排序 再按極角從小到大排序 {if(Quadrant(a)==Quadrant(b))//返回值就是象限return cmp1(a,b);//return cmp2(a,b)else Quadrant(a)<Quadrant(b); }注意要開long long
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=5e3+10; struct point{ll x,y; }p[maxn],q[maxn]; bool cmp(point a,point b){return a.x*b.y-a.y*b.x>0; } int main(){int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%lld%lld",&p[i].x,&p[i].y);}double mmin=1e18;for(int i=1;i<=n;i++){int cnt=0;for(int j=1;j<=n;j++){if(i==j) continue;q[cnt].x=p[j].x-p[i].x;q[cnt++].y=p[j].y-p[i].y;}sort(q,q+cnt,cmp);//級角排序for(int j=0;j<cnt;j++){double ans=0.5*abs(q[j].x*q[(j+1)%cnt].y-q[j].y*q[(j+1)%cnt].x);//計算相鄰向量形成三角形的面積mmin=min(ans,mmin);}}printf("%.3lf\n",round(mmin*1000)/1000);return 0; }其他
L3-013 非常彈的球 (30 分)
假設森森是一個質點,以森森為原點設立坐標軸,則森森位于(0, 0)點。
小球質量為w/100 千克(kg),重力加速度為9.8米/秒平方(m/s2)。
森森在地上用力彈球的過程可簡化為球從(0, 0)點以某個森森選擇的角度an**g (0<an**g<π/2) 向第一象限拋出,拋出時假設動能為1000 焦耳(J)。
小球在空中僅受重力作用,球縱坐標為0時可視作落地,落地時損失p%動能并反彈。
地面可視為剛體,忽略小球形狀、空氣阻力及摩擦阻力等。
求:最遠的投擲距離,保留3位小數
被簡單物理題難住。。。
求距離,那就需要知道速度和時間(s=vt)
假設以夾角 α \alpha α拋出,初速度為 v 0 v_0 v0?,將其沿水平和豎直兩個方向分解。
豎直方向:
只受重力,做勻減速運動(加速度為 g g g),向上減速為0有:
v 0 s i n α = g t v_0sin\alpha=gt v0?sinα=gt,則 t = v 0 s i n α g t=\frac{v_0sin\alpha}{g} t=gv0?sinα?
從拋出到第一次落地的總時間即為 T = 2 t = 2 v 0 s i n α g T=2t=\frac{2v_0sin\alpha}{g} T=2t=g2v0?sinα?
水平方向:
s = v 0 T c o s α = v 0 2 s i n 2 α g s=v_0Tcos\alpha=\frac{v_0^2sin2\alpha}{g} s=v0?Tcosα=gv02?sin2α?
當 α = π 4 \alpha=\frac{\pi}{4} α=4π?時,s取最大值, s = v 0 2 g = 2 E k m g s=\frac{v_0^2}{g}=\frac{2E_k}{mg} s=gv02??=mg2Ek??
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; const int INF=0x3f3f3f3f; typedef long long ll; const int mod=1e9+7; int main(){double w,p;scanf("%lf%lf",&w,&p);w/=100;double s=0,Ek=1000,g=9.8;while(Ek>=1e-9){//zs+=2*Ek/(w*g);Ek-=p*Ek/100;} printf("%.3lf\n",s);return 0; }L1-050 倒數第N個字符串 (15 分)
給定一個完全由小寫英文字母組成的字符串等差遞增序列,該序列中的每個字符串的長度固定為 L,從 L 個 a 開始,以 1 為步長遞增。例如當 L 為 3 時,序列為 { aaa, aab, aac, …, aaz, aba, abb, …, abz, …, zzz }。這個序列的倒數第27個字符串就是 zyz。對于任意給定的 L,本題要求你給出對應序列倒數第 N 個字符串。
對于長度為 n n n的字符串,可得到的序列共有 2 6 n 26^n 26n個字符串
類似于26進制,倒數第一個字母(冪次為0),每變換一次,字符串增加1個;倒數第二個字母(冪次為1),每變換一次,字符串增加26個;倒數第三個字母(冪次為2),每變換一次,字符串增加 2 6 2 26^2 262個。
求倒數第 N N N個字符串,即求第 2 6 L ? N 26^L-N 26L?N個,將 2 6 L ? N 26^L-N 26L?N轉換成26進制,每一位輸出對應的字母即可。
但是要注意求倒數第N個,即第0個的情況,或者是轉換為26進制后,長度不足L,需補足前綴0
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+10; int ksm(int a,int b){int ans=1;while(b){if(b&1) ans=ans*a;a=a*a;b>>=1;}return ans; } vector<int>s; int main(){int l,n;cin>>l>>n;int res=ksm(26,l)-n;while(res){s.push_back(res%26);res/=26; }int len=s.size();for(int i=len;i<=l-len;i++) s.push_back(0);//補前綴0for(int i=l-1;i>=0;i--){char ch=s[i]+'a';cout<<ch;}cout<<endl;return 0; }總結
- 上一篇: 乔春洋:品牌文化的三大内涵
- 下一篇: Selenium 自动化测试从0实战经验