暑假集训CSP提高模拟19
\(T1\) P173. 数字三角形 \(20pts\)
- 原题: CF1517C Fillomino 2
- 部分分
-
\(20pts\) :剪枝搜索。
点击查看代码
int p[510],c[510],ans[510][510],dx[5]={0,1,-1,0,0},dy[5]={0,0,0,-1,1}; void dfs(int pos,int x,int y,int num,int n) { if(pos==n+1) { for(int i=1;i<=n;i++) { for(int j=1;j<=i;j++) { cout<<ans[i][j]<<" "; } cout<<endl; } exit(0); } else { if(num==0) { dfs(pos+1,pos+1,pos+1,c[pos+1],n); } else { for(int i=1;i<=4;i++) { int nx=x+dx[i]; int ny=y+dy[i]; if(1<=nx&&nx<=n&&1<=ny&&ny<=x&&ans[nx][ny]==0) { ans[nx][ny]=p[pos]; dfs(pos,nx,ny,num-1,n); ans[nx][ny]=0; } } } } } int main() { int n,i,j; cin>>n; memset(ans,-1,sizeof(ans)); for(i=1;i<=n;i++) { cin>>p[i]; ans[i][i]=p[i]; c[i]=p[i]-1; } for(i=1;i<=n;i++) { for(j=1;j<=i-1;j++) { ans[i][j]=0; } } dfs(1,1,1,c[1],n); return 0; }
-
- 正解
-
通过打表,我们可以发现合法方案中同一个数向右延伸一定不优,向左延伸比向下延伸更优。
-
所以填数时优先向左延伸,否则向下延伸。
点击查看代码
int p[510],c[510],ans[510][510]; void dfs(int pos,int x,int y,int num,int n) { if(pos==n+1) { for(int i=1;i<=n;i++) { for(int j=1;j<=i;j++) { cout<<ans[i][j]<<" "; } cout<<endl; } exit(0); } else { if(num==0) { dfs(pos+1,pos+1,pos+1,c[pos+1],n); } else { if(y-1>=1&&ans[x][y-1]==0) { ans[x][y-1]=p[pos]; dfs(pos,x,y-1,num-1,n); } else { if(x+1<=n&&ans[x+1][y]==0) { ans[x+1][y]=p[pos]; dfs(pos,x+1,y,num-1,n); } } } } } int main() { int n,i; cin>>n; for(i=1;i<=n;i++) { cin>>p[i]; ans[i][i]=p[i]; c[i]=p[i]-1; } dfs(1,1,1,c[1],n); return 0; }
-
\(T2\) P160. 那一天她离我而去 \(0pts\)
- 部分分
- \(76pts\) : \(Dijkstra\) 求解最小环。
-
枚举 \(1\) 的所有出边,钦定这条边是环上的边,删掉这条边后再求到这个点的最短路长度,加上被删的边的长度与答案取 \(\min\) 即可。
-
若不限制起点,则需要枚举边 \((u,v,w) \in E\) ,删掉这条边后 \(u \to v\) 的最短路长度 \(+w\) 与答案取 \(\min\) 即可。时间复杂度为 \(O(m(n+m)\log n)\) 。
点击查看代码
struct node { int nxt,to,w; }e[80010]; int head[10010],vis[10010],dis[10010],brokeu,brokev,cnt=0; vector<pair<int,int> >broke; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } bool check(int u,int v) { return ((u==brokeu&&v==brokev)||(u==brokev&&v==brokeu)); } void dijkstra(int s) { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); priority_queue<pair<int,int> >q; dis[s]=0; q.push(make_pair(-dis[s],-s)); while(q.empty()==0) { int x=-q.top().second; q.pop(); if(vis[x]==0) { vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(check(x,e[i].to)==0&&dis[e[i].to]>dis[x]+e[i].w) { dis[e[i].to]=dis[x]+e[i].w; q.push(make_pair(-dis[e[i].to],-e[i].to)); } } } } } int main() { int t,n,m,ans,u,v,w,i,j; cin>>t; for(j=1;j<=t;j++) { cnt=0; ans=0x7f7f7f7f; broke.clear(); memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); cin>>n>>m; for(i=1;i<=m;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); if(u==1) { broke.push_back(make_pair(v,w)); } if(v==1) { broke.push_back(make_pair(u,w)); } } for(i=0;i<broke.size();i++) { brokeu=1; brokev=broke[i].first; dijkstra(1); if(dis[brokev]!=0x3f3f3f3f) { ans=min(ans,dis[brokev]+broke[i].second); } } cout<<(ans>=0x7f7f7f7f?-1:ans)<<endl; } return 0; }
-
- \(76pts\) : \(Dijkstra\) 求解最小环。
- 正解
\(T3\) P175. 哪一天她能重回我身边 \(20pts\)
- 部分分
-
\(20pts\) :剪枝搜索。
点击查看代码
const ll p=998244353; ll x[100010],y[100010],cnt[200010],ans,sum; void dfs(ll pos,ll num,ll n) { if(pos==n+1) { if(num<ans) { ans=num; sum=1; } else { if(num==ans) { sum=(sum+1)%p; } } } else { cnt[x[pos]]++; if(cnt[x[pos]]==1) { dfs(pos+1,num,n); } cnt[x[pos]]--; cnt[y[pos]]++; if(cnt[y[pos]]==1) { dfs(pos+1,num+1,n); } cnt[y[pos]]--; } } int main() { ll t,n,i,j; cin>>t; for(j=1;j<=t;j++) { cin>>n; for(i=1;i<=n;i++) { cin>>x[i]>>y[i]; } ans=0x7f7f7f7f; sum=0; dfs(1,0,n); if(ans==0x7f7f7f7f) { cout<<"-1 -1"<<endl; } else { cout<<ans<<" "<<sum<<endl; } } return 0; }
-
- 正解
\(T4\) P174. 单调区间 \(60pts\)
- 原题: CF1693D Decinc Dividing
- 部分分
-
\(60pts\) : \(O(n^{2})\) 枚举左右端点,剪枝搜索判断合不合法。
点击查看代码
ll a[200010],b[200010]; vector<ll>s1,s2; bool dfs(ll pos,ll n) { if(pos==n+1) { return true; } else { ll flag=0; if(s1.size()==0||s1[s1.size()-1]>b[pos]) { s1.push_back(b[pos]); flag|=dfs(pos+1,n); s1.pop_back(); } if(s2.size()==0||s2[s2.size()-1]<b[pos]) { s2.push_back(b[pos]); flag|=dfs(pos+1,n); s2.pop_back(); } return flag; } } bool check(ll l,ll r) { for(ll i=l;i<=r;i++) { b[i-l+1]=a[i]; } return dfs(1,r-l+1); } int main() { ll n,ans=0,i,j; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } for(i=1;i<=n;i++) { for(j=i;j<=n;j++) { ans+=check(i,j); } } cout<<ans<<endl; return 0; }
-
- 正解
总结
- \(T1\) 赛时把结论猜出来后一直在想怎么处理边界和怎么进行 \(hack\) ,到最后也没打代码。
- \(T2\) 因为提交前忘再编译一遍了,把分号打成了冒号,因 \(CE\) 挂了 \(23pts\) ;因为知道是板子,但自己没学所以干脆没去想 \(Dijkstra\) 怎么求解最小环,可能是学的东西太多了导致的?