USACO 2015 January S 題解
阿新 • • 發佈:2022-01-03
2015 January S
P3114 [USACO15JAN]Stampede S
本題和區間覆蓋有點類似,每頭牛所對應的區間就是這頭牛能被看到的時間
區間以 \(y\) 座標進行降序排序,直接區間覆蓋即可
注意,本題必須用左閉右開區間儲存,因為在結束的那一刻是看不見的(已經離開了)
為了儲存這些區間,我們採用離散化的方式儲存(主要是離散化後可以暴力覆蓋)
程式碼:
#include <map> #include <set> #include <cstdio> #include <algorithm> typedef long long ll; using namespace std; const int inf=0x3f3f3f3f; const int N=5e4+7; struct node { int b,e,y; inline bool operator < (const node &p) const { return y>p.y; } }a[N]; map<ll,int> h; set<ll> s; set<int> ans; int vis[N<<2]; int n,cnt; signed main() { scanf("%d",&n); for(int i=1;i<=n;++i) { ll x,y,r; scanf("%lld%lld%lld",&x,&y,&r); a[i]={(-x-1)*r,(-x-1)*r+r-1,y}; // 建立區間 s.insert(a[i].b),s.insert(a[i].e),s.insert(a[i].e+1); } for(set<ll>::iterator it=s.begin();it!=s.end();++it) h[*it]=++cnt; // 離散化 sort(a+1,a+1+n); // 以 y 座標進行降序排序 for(int i=1;i<=n;++i) { a[i].b=h[a[i].b],a[i].e=h[a[i].e]; for(int j=a[i].b;j<=a[i].e;++j) vis[j]=i; // 暴力覆蓋 } for(int i=0;i<(N<<2);++i) ans.insert(vis[i]); // 判斷有多少頭牛可以被看見 printf("%d",ans.size()-1); // -1 是因為把看不到牛的時間點也算進去了 return 0; }
P3115 [USACO15JAN]Cow Routing S
對每條航線進行建邊處理,同時記錄兩個城市之間的城市數,然後跑最短路即可
注意,本題由於邊數比較多,建議用鄰接矩陣儲存
程式碼:
#include <queue> #include <cstdio> #include <vector> typedef long long ll; using namespace std; const ll inf_ll=1e16; const int inf_int=0x3f3f3f3f; const int N=1e3+7; struct node { ll w; int num,u; inline bool operator < (const node &y) const { return w>y.w || (w==y.w && num>y.num); } }; vector<int> plane; pair<ll,int> edge[N][N]; ll dis[N]; int pre[N]; int s,e,n; inline void Dijkstra(int s) { priority_queue<node> q; for(int i=1;i<N;++i) dis[i]=inf_ll,pre[i]=-1; dis[s]=0,pre[s]=0; q.push({0,0,s}); while(!q.empty()) { node c=q.top(); q.pop(); if(c.w!=dis[c.u] || c.num!=pre[c.u]) continue; int u=c.u; for(int v=1,num;v<N;++v) { ll w=edge[u][v].first; num=edge[u][v].second; if(dis[v]>dis[u]+w || (dis[v]==dis[u]+w && pre[v]>pre[u]+num) ) { dis[v]=dis[u]+w; pre[v]=pre[u]+num; q.push({dis[v],pre[v],v}); } } } } signed main() { scanf("%d%d%d",&s,&e,&n); for(int i=1;i<N;++i) for(int j=1;j<N;++j) edge[i][j]={inf_ll,inf_int}; for(int i=1,num;i<=n;++i) { ll price; scanf("%lld%d",&price,&num); plane.clear(); for(int v;num;--num) { scanf("%d",&v); for(int j=0,u;j<plane.size();++j) { u=plane[j]; if(edge[u][v].first>price) edge[u][v]={price,plane.size()-j}; // 當前點到 u 經過這條航線要經過 plane.size()-j 個點 else if(edge[u][v].first==price && edge[u][v].second>plane.size()-j) edge[u][v]={price,plane.size()-j}; } plane.push_back(v); } } Dijkstra(s); if(pre[e]==-1) puts("-1 -1"); else printf("%lld %d",dis[e],pre[e]); return 0; }
P3116 [USACO15JAN]Meeting Time S
先考慮暴力
設 \(b_{i,j}\) 為 \(\tt{Bessie}\) 能否用 \(j\) 的使時間到達第 \(i\) 個點, \(e_{i,j}\) 為 \(\tt{Elesie}\) 能否用 \(j\) 的使時間到達第 \(i\) 個點
我們通過廣搜遍歷每一個點(此題為有向無環圖),狀態轉移方程為:
\[b_{v,i+w1}|=b_{u,i} \\ e_{v,i+w2}|=e_{u,i} \]可以拿到部分分數
考慮優化,暴力的主要缺點是轉移完時候可能還有路徑到達這個點,造成多次計算
那我們可不可以先把所有可以到達這個點的路徑全部遍歷完再轉移,顯然,我們可以用拓撲排序實現
程式碼如下:
#include <queue>
#include <cstdio>
#include <vector>
using namespace std;
const int N=1e2+7;
struct node {
int v,w1,w2;
};
vector<node> edge[N];
queue<int> q;
bool bessie[N][N*N],elsie[N][N*N];
int indeg[N];
int n,m;
inline void TopoSort() {
bessie[1][0]=elsie[1][0]=true;
for(int i=1;i<=n;++i)
if(!indeg[i])
q.push(i);
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=0,v,w1,w2;i<edge[u].size();++i) {
v=edge[u][i].v;
w1=edge[u][i].w1,w2=edge[u][i].w2;
for(int i=0;i+w1<N*N;++i)
bessie[v][i+w1]|=bessie[u][i];
for(int i=0;i+w2<N*N;++i)
elsie[v][i+w2]|=elsie[u][i]; // 狀態轉移
--indeg[v];
if(!indeg[v])
q.push(v);
}
}
}
signed main() {
scanf("%d%d",&n,&m);
for(int i=1,u,v,w1,w2;i<=m;++i) {
scanf("%d%d%d%d",&u,&v,&w1,&w2);
edge[u].push_back({v,w1,w2});
++indeg[v];
}
TopoSort();
for(int i=0;i<N*N;++i)
if(bessie[n][i] && elsie[n][i])
return printf("%d",i),0;
puts("IMPOSSIBLE");
return 0;
}