[USACO 2016 Dec Gold] Tutorial
阿新 • • 發佈:2018-09-10
lose efi -a clas pan tar per for 決定
Link:
傳送門
A:
貪心從小到大插入,用並查集維護連通性
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef double db; typedef long long ll; typedef pair<int,int> P; const int MAXN=1e3+10; int n,tot,cnt,f[MAXN];P dat[MAXN]; struct edge{int x,y;ll w;}e[MAXN*MAXN]; ll sqr(int x){returnProblem A1ll*x*x;} bool cmp(edge x,edge y){return x.w<y.w;} int find(int x){return f[x]==x?x:f[x]=find(f[x]);} int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&dat[i].X,&dat[i].Y),f[i]=i; for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) e[++tot]={i,j,sqr(dat[i].X-dat[j].X)+sqr(dat[i].Y-dat[j].Y)}; sort(e+1,e+tot+1,cmp); cnt=n; for(int i=1;i<=tot;i++) { int px=find(e[i].x),py=find(e[i].y); if(px!=py) f[px]=py,cnt--; if(cnt==1) return printf("%lld",e[i].w),0; } return 0; }
B:
$dp[1...n][1...m][0/1]$,其中0/1表示當前在哪一邊
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef double db; typedef long long ll; typedef pair<int,int> P; const int MAXN=1e3+10,INF=0x3f3f3f3f; ll dp[MAXN][MAXN][2]; int n,m;P dat[MAXN*2]; ll sqr(int x){return 1ll*x*x;} ll dist(int a,int b){return sqr(dat[a].X-dat[b].X)+sqr(dat[a].Y-dat[b].Y);} int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n+m;i++) scanf("%d%d",&dat[i].X,&dat[i].Y); memset(dp,0x3f,sizeof(dp)); dp[1][0][0]=0; for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k<2;k++) { if(i) dp[i+1][j][0]=min(dp[i+1][j][0],dp[i][j][0]+dist(i,i+1)); if(j) dp[i+1][j][0]=min(dp[i+1][j][0],dp[i][j][1]+dist(n+j,i+1)); if(i) dp[i][j+1][1]=min(dp[i][j+1][1],dp[i][j][0]+dist(i,n+j+1)); if(j) dp[i][j+1][1]=min(dp[i][j+1][1],dp[i][j][1]+dist(n+j,n+j+1)); } printf("%lld",dp[n][m][0]); return 0; }Problem B
C:
可以明顯發現是最短路模型,但如果將一個點能不轉彎走到的所有點的邊都連上明顯$TLE$
那麽在跑最短路時多記錄一維當前方向即可,每次轉移判斷是否要轉向來決定是否增加代價
這樣只要與不穿過其他點就能到達的點連邊就行了
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef double db; typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10,INF=0x3f3f3f3f; int n,x,y,h1[MAXN],h2[MAXN],dist[MAXN][2],tot; struct edge{int nxt,to;}e1[MAXN<<2],e2[MAXN<<2]; struct node{int x,y,id;}tmp[MAXN],dat[MAXN]; struct Queue{int w,x,dir;}; bool cmp1(node a,node b){return a.x<b.x;} bool cmp2(node a,node b){return a.y<b.y;} bool operator < (Queue a,Queue b){return a.w>b.w;} void add1(int x,int y) { e1[++tot]={h1[x],y};h1[x]=tot; e1[++tot]={h1[y],x};h1[y]=tot; } void add2(int x,int y) { e2[++tot]={h2[x],y};h2[x]=tot; e2[++tot]={h2[y],x};h2[y]=tot; } priority_queue<Queue> q; int main() { scanf("%d",&n);n+=2; scanf("%d%d",&x,&y);dat[1]=tmp[1]={x,y,1}; scanf("%d%d",&x,&y);dat[n]=tmp[n]={x,y,n}; for(int i=2;i<n;i++) scanf("%d%d",&dat[i].x,&dat[i].y),dat[i].id=i,tmp[i]=dat[i]; sort(tmp+1,tmp+n+1,cmp1); for(int i=1;i<n;i++) if(tmp[i].x==tmp[i+1].x) add1(tmp[i].id,tmp[i+1].id); sort(tmp+1,tmp+n+1,cmp2); for(int i=1;i<n;i++) if(tmp[i].y==tmp[i+1].y) add2(tmp[i].id,tmp[i+1].id); memset(dist,0x3f,sizeof(dist)); dist[1][0]=dist[1][1]=0; q.push(Queue{0,1,0});q.push(Queue{0,1,1}); while(!q.empty()) { Queue t=q.top();q.pop(); if(dist[t.x][t.dir]<t.w) continue; for(int i=h1[t.x];i;i=e1[i].nxt) if(dist[e1[i].to][0]>dist[t.x][t.dir]+(t.dir!=0)) dist[e1[i].to][0]=dist[t.x][t.dir]+(t.dir!=0),q.push(Queue{dist[e1[i].to][0],e1[i].to,0}); for(int i=h2[t.x];i;i=e2[i].nxt) if(dist[e2[i].to][1]>dist[t.x][t.dir]+(t.dir!=1)) dist[e2[i].to][1]=dist[t.x][t.dir]+(t.dir!=1),q.push(Queue{dist[e2[i].to][1],e2[i].to,1}); } int res=min(dist[n][0],dist[n][1]); printf("%d",res==INF?-1:res); return 0; }Problem C
不過看了題解發現由於:
1、同一行/列可以直達任一點
2、每次代價增加必然是行列間轉化
從而也可以僅考慮坐標,離散化後對於點$(x,y)$將$x$行和$y$列連邊即可
[USACO 2016 Dec Gold] Tutorial