(SWERC 2019-20)A G K

好久没写博客没好好打了。记录一下三道中档题。读题能读死。太⛏了。

A. Environment-Friendly Travel

题意:给定二维平面起点、终点,和一些中间点。可以通过最贵的方式从起点到中间点,从中间点到终点。中间点之间存在某些单价便宜的交通方式。费用为距离乘单价。

设置了一个最远距离,点之间距离为欧式距离向上取整。问从起点到终点不超过最远距离的最小费用。不存在输出-1.

想法:如果没有最远距离的限制,那这道题就是一个dijkstra最短路了。注意到最远距离不超过100,点也就1000个,那就存下到某个点距离为某数的合法状态,不断dijkstra bfs扩展即可。

我参考的网上的,用bfs求最短路,不太会分析复杂度V·E。的确跑的慢1000ms以上。测了C队的dijkstra跑的飞快100ms。

看来还是要堆优化。

#include <bits/stdc++.h>
#ifndef ONLINE_JUDGE
#define debug(x) cout << #x << ": " << x << endl
#else
#define debug(x)
#endif
using namespace std;
typedef long long ll;
const int maxn=1e3+7;
const int maxe=1e6+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;

int dp[maxn][110],B,T,n;
int c[110];
struct point
{
    int x,y;
}p[maxn];

vector<pair<int,int> >g[maxn];

int dist(int i,int j)
{
    double dis=sqrt(double((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y)));
    return (int)ceil(dis);
}
struct node
{
    int id,dis,cost,pre;
};
struct edge
{
    int to,nxt,dis,cost;
}e[maxe<<1];
int head[maxn],tot=0;
void addedge(int u,int v,int dis,int cost)
{
    e[++tot].to=v;
    e[tot].dis=dis;
    e[tot].cost=cost;
    e[tot].nxt=head[u];
    head[u]=tot;
}

int ans;
void bfs(int s,int t)
{
    queue<node>q;
    ans=inf;
    q.push({s,0,0,-1});
    while(!q.empty())
    {
        node cur=q.front();q.pop();
        int u=cur.id;
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(v==cur.pre) continue;
            if(cur.dis+e[i].dis>B) continue;
            if(v==t)
            {
                ans=min(ans,cur.cost+e[i].cost);
                continue;
            }
            if(dp[v][cur.dis+e[i].dis]>cur.cost+e[i].cost)
            {
                dp[v][cur.dis+e[i].dis]=cur.cost+e[i].cost;
                q.push({v,cur.dis+e[i].dis,cur.cost+e[i].cost,u});
            }
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int xs,ys,xd,yd;
    cin>>xs>>ys>>xd>>yd;
    cin>>B>>c[0]>>T;
    for(int i=1;i<=T;++i) cin>>c[i];
    cin>>n;
    for(int i=0,l,id,type;i<n;++i)
    {
        cin>>p[i].x>>p[i].y>>l;
        while(l--)
        {
            cin>>id>>type;
            g[i].push_back({id,type});
        }
    }
    for(int i=0;i<n;++i)
    {
        for(int j=0;j<g[i].size();++j)
        {
            auto u=g[i][j];
            int id=u.first,type=u.second,dis=dist(i,id);
            addedge(i,id,dis,c[type]*dis);
            addedge(id,i,dis,c[type]*dis);
        }
    }
    p[n]={xs,ys};
    p[n+1]={xd,yd};
    for(int i=0;i<n;++i)
    {
        int dis=dist(i,n);
        addedge(i,n,dis,dis*c[0]);
        addedge(n,i,dis,dis*c[0]);
        dis=dist(i,n+1);
        addedge(i,n+1,dis,dis*c[0]);
        addedge(n+1,i,dis,dis*c[0]);
    }
    int dis=dist(n,n+1);
    addedge(n,n+1,dis,dis*c[0]);
    addedge(n+1,n,dis,dis*c[0]);
    memset(dp,0x3f,sizeof(dp));
    bfs(n,n+1);
    if(ans==inf) ans=-1;
    cout<<ans<<endl;
    return 0;
}
View Code

G. Swapping Places

题意:S个动物名字,L种两两可以交换的关系(并不传递),N个动物的序列。求字典序最小的动物名字序列。

想法:发现能交换是一种关系,不能交换也是一种关系,就是这些动物之间必然存在先后顺序,利用先后顺序,想到可以拓扑排序。然后O(N*S)建边,但要求字典序最小,需要处理出各个字符对应的字典序排名序号,同时拓扑排序的时候带权。

O(n*s+nlogn)。

#include <bits/stdc++.h>
#ifndef ONLINE_JUDGE
#define debug(x) cout << #x << ": " << x << endl
#else
#define debug(x)
#endif
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;

map<string,int>mp;
int in[maxn],idx[maxn],last[220],g[220][220],s,l,n;
vector<int>to[maxn];
string ans[maxn];

struct node
{
    int id,val;
    bool operator < (const node &rhs) const
    {
        return val>rhs.val;
    }
};

void topsort()
{
    priority_queue<node>pq;
    for(int i=1;i<=n;++i)
        if(!in[i]) pq.push({i,idx[i]});
    while(!pq.empty())
    {
        node cur=pq.top();pq.pop();
        cout<<ans[cur.val]<<' ';
        for(int i=0;i<to[cur.id].size();++i)
        {
            int v=to[cur.id][i];
            in[v]--;
            if(!in[v]) pq.push({v,idx[v]});
        }
    }
    cout<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>s>>l>>n;
    string str;
    for(int i=1;i<=s;++i)
    {
        cin>>str;
        mp[str]++;
        last[i]=-1;
    }
    int k=1;
    for(auto &u:mp)
    {
        u.second=k;
        ans[k]=u.first;
        k++;
    }
    for(int i=1;i<=l;++i)
    {
        cin>>str;
        int u=mp[str];
        cin>>str;
        int v=mp[str];
        g[u][v]=g[v][u]=1;
    }
    for(int i=1;i<=n;++i)
    {
        cin>>str;
        int now=mp[str];
        idx[i]=now;
        for(int j=1;j<=s;++j)
        {
            if(g[j][now]) continue;
            if(last[j]==-1) continue;
            to[last[j]].push_back(i);
            in[i]++;
        }
        last[now]=i;
    }
    topsort();
    return 0;
}
View Code

K. Birdwatching

题意:挺绕的。就是求有向图到目标点T只有通过邻接边一条路的点。

想法:显然与T直接相连的点,是在我们的考虑范围中的,但并不是特别好搞,不能直接去掉这些边,再逐个dfs,会互相影响。

做法是找出直接相连的点,叫做嫌疑点,这些边不去连接。其他的边反向连接。然后对这些嫌疑点逐个dfs,并记录每个点能从反向建图后的几个嫌疑点到达。能从反向建图后的嫌疑点到达,说明这个点正向能到嫌疑点,也就顺势能到目标点。

能从多个嫌疑点到达,这样的点显然排除,dfs的同时防止环。

最后答案就是只能从一个嫌疑点到达目标点,即从自己到达目标点的嫌疑点就是答案。

#include <bits/stdc++.h>
#ifndef ONLINE_JUDGE
#define debug(x) cout << #x << ": " << x << endl
#else
#define debug(x)
#endif
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;

vector<int>g[maxn],ans;
set<int>f[maxn],st;

void dfs(int u,int t)
{
    if(f[u].size()>1||f[u].count(t)) return;
    f[u].insert(t);
    for(auto it:g[u]) dfs(it,t);
}

int main()
{
    int n,m,t;
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1; i<=m; i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(v==t) st.insert(u);
        else g[v].push_back(u);
    }
    for(auto &it:st)
        dfs(it,it);
    for(auto &it:st)
        if(f[it].size()==1) ans.push_back(it);
    printf("%d
",ans.size());
    for(auto &it:ans) printf("%d
",it);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Zzqf/p/12691997.html