zoukankan      html  css  js  c++  java
  • Gym101482 NWERC 2014(队内训练第4场)

    -----------------------前面的两场感觉质量不高,就没写题解-----------------------------

    A .Around the Track

    pro:给定内多边形A和外多边形B,求最短路径,蛮子路径再A之外,B之内。

    sol:如果没有B,就是求凸包,有了B,我们在做凸包的时候,有形如“a-b-c,b在内部,删去b,连接a-c的操作”,如果a-c和B不相交,直接删去b,否则用B的凸包代替b处。

    #include <bits/stdc++.h>
    using namespace std;
    #define x first
    #define y second
    typedef pair<int, int> Point;
    typedef long long LL;
    const int maxn=110;
    bool operator != (Point b, Point c)
    {
        return (b.x!=c.x || b.y!=c.y);
    }
    double dis(Point b, Point c);
    struct polygon
    {
        int n;
        bool cannotdel[maxn];
        Point p[maxn];
    
        polygon():n(0){}
    
        void read()
        {
            scanf("%d", &n);
            for (int i=1; i<=n; ++i)
                scanf("%d%d", &p[i].x, &p[i].y);
            p[0]=p[n];
            p[n+1]=p[1];
            for (int i=1; i<=n; ++i) cannotdel[i]=false;
        }
    
        double track()
        {
            double ans=0;
            for (int i=1; i<=n; ++i)
                ans+=dis(p[i], p[i+1]);
            return ans;
        }
    
        void del(int pos)
        {
            for (int i=pos; i<n; ++i)
            {
                p[i]=p[i+1];
                cannotdel[i]=cannotdel[i+1];
            }
            --n;
            p[n+1]=p[1];
            p[0]=p[n];
        }
    
        void insert(int posl, int posr, vector<Point> &newp)
        {
            int m=posl+n-posr+1+newp.size();
            for (int i=0; i<(n-posr+1); ++i)
            {
                p[m-i]=p[n-i];
                cannotdel[m-i]=cannotdel[n-i];
            }
            n=m; m=newp.size();
            for (int i=0; i<m; ++i)
            {
                p[posl+1+i]=newp[i];
                cannotdel[posl+1+i]=true;
            }
            p[n+1]=p[1];
            p[0]=p[n];
        }
    
        void print()
        {
            puts("======================");
            for (int i=1; i<=n+1; ++i)
                printf("%d %d
    ", p[i].x, p[i].y);
        }
    };
    polygon inner, outer;
    Point pp[maxn];
    Point globalps;
    set<Point> innerpoint;
    vector<Point> newp;
    inline LL sqr(LL x)
    {
        return x*x;
    }
    LL det(Point b, Point c, Point o)
    {
        return (b.x-o.x)*(c.y-o.y)-(b.y-o.y)*(c.x-o.x);
    }
    double dis(Point b, Point c)
    {
        return sqrt(sqr(b.x-c.x)+sqr(b.y-c.y));
    }
    LL sdis(Point b, Point c)
    {
        return (sqr(b.x-c.x)+sqr(b.y-c.y));
    }
    bool intriangle(Point &b, Point &c, Point &d, Point &o)
    {
        return (abs(det(b, c, o))+abs(det(b, d, o))+abs(det(c, d, o)))==abs(det(b, c, d));
    }
    bool cmp(Point b, Point c)
    {
        LL tmp=det(b, c, globalps);
        if (tmp!=0) return tmp<0;
        return sdis(b, globalps)<sdis(c, globalps);
    }
    void convex(vector<Point> &p, Point &ps, Point &pf)
    {
        globalps=ps;
        sort(p.begin(), p.end(), cmp);
        int n=p.size();
        for (int i=1; i<=n; ++i) pp[i]=p[i-1];
        pp[++n]=pf;
        pp[0]=ps;
        int m=0;
        for (int i=1; i<=n; ++i)
        {
            while (m>0 && det(pp[i], pp[m], pp[m-1])<0) --m;
            pp[++m]=pp[i];
        }
        p.resize(m-1);
        for (int i=1; i<m; ++i) p[i-1]=pp[i];
    }
    void solve()
    {
        for (int i=1; i<=inner.n; ++i)
            innerpoint.insert(inner.p[i]);
    
        while (1)
        {
            bool flag=false;
            for (int i=1; i<=inner.n; ++i)
                if (!inner.cannotdel[i] && det(inner.p[i+1], inner.p[i-1], inner.p[i])<0)
                {
                    newp.clear();
                    for (int j=1; j<=outer.n; ++j)
                        if (intriangle(inner.p[i-1], inner.p[i], inner.p[i+1], outer.p[j]))
                            if (innerpoint.count(outer.p[j])==0)
                                newp.push_back(outer.p[j]);
    
                    for (int j=1; j<=inner.n; ++j)
                        if (inner.p[j]!=inner.p[i-1] && inner.p[j]!=inner.p[i] && inner.p[j]!=inner.p[i+1]
                            && intriangle(inner.p[i-1], inner.p[i], inner.p[i+1], inner.p[j]))
                                newp.push_back(inner.p[j]);
    
                    if (newp.size()==0) { flag=true; inner.del(i); break; }
                    convex(newp, inner.p[i-1], inner.p[i+1]);
    
                    flag=true;
                    for (auto &p:newp)
                        if (innerpoint.count(p)) { flag=false; break; }
    
                    if (!flag) continue;
                    for (auto &p:newp) innerpoint.insert(p);
                    inner.insert(i-1, i+1, newp);
                }
            if (!flag) break;
        }
        printf("%.7lf
    ", inner.track());
    }
    int main()
    {
        inner.read();
        outer.read();
        solve();
        return 0;
    }
    View Code

    B .Biking Duck

    pro:给定二维平面,已知有N个位置有自行车站,以及地图边界都是自行车站,自行车可以从一个站到另一个站。在地图上走路或者骑自行车都是走直线。给定终点起点,求从从起点到终点的最小时间。

    sol:路径不可能是曲曲折折的,这样性价比不高。 只有几种情况:

       1:骑车可能要绕路,没必要骑车, 直接走路,起点->终点。

       2:骑一次,起点走路->到站->到站->走路到终点。

    而骑车要考虑边界上的车站,我们可以利用对称的知识,知道以边界为直线对称后,两点间直线最短,与对称轴的交点,是凹函数的分界线,这个可以用三分求。

    ...写起来应该很烦,告辞。

    C .Cent Savings

    pro:按顺序给定N个商品,相邻的可以分到同一组,你可以最多分成D+1组,每一组的价格四舍五入,求最小价格。N<2e3,D<20;

    sol:由于的相邻的分组,就DP即可,O(N^2*D)。 单调队列优化O(N*D);

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=2010;
    int dp[maxn][30];//dp[i][j]
    int a[maxn], sum[maxn];
    inline f(int x){return (x / 10 * 10 + (x % 10 <= 4 ? 0 : 10));}
    int main()
    {
        int n, d;
        cin >> n >> d;
        for(int i = 1; i <= n; i++)cin >> a[i], sum[i] = a[i] + sum[i - 1];
        for(int i = 1; i <= n; i++)dp[i][1] = f(sum[i]);
        for(int j = 2; j <= d + 1; j++)
        {
            for(int i = j; i <= n; i++)
            {
                int tmp = 1e9 + 7;
                for(int k = j - 1; k <= i - 1; k++)
                {
                    tmp = min(tmp, dp[k][j - 1] + f(sum[i] - sum[k]));
                }
                dp[i][j] = tmp;
            }
        }
        int ans = 1e9 + 7;
        for(int i = 1; i <= d + 1 && i <= n; i++)ans = min(ans, dp[n][i]);
        cout<<ans<<endl;
        return 0;
    }

     

    D .Digi Comp II

    pro:给定M个开关,每个开关有初始状态(L或者R),每个开关有两个走向,分别指向左边对应的开关和右边对应的开关。 一个球走到当前开关,会走向当前状态指向的方向,并且使当前的状态改变。 问N个球从1号出发,最终每个开关的状态。 给定的关系是个DAG,除了0号都有两个出度,可以看成左右儿子。

    sol:模拟一下,不难发现,如果X个球经过i节点,那么将会有X/2+X&1次走向初始方向,X/2次走向另外一个方向。即是topo排序一下可以搞。

    这题WA的蛮多的,有两个注意;

             1:不一定只要1号点入度为0,题目只保证了除0之外,出度为2;没保证入度(这一点感觉题目的确误导了很多读题人)。 所以必须开始把入度为0的都加进queue去,不然有的点跑不到。

             2:左右儿子一样的情况下,如果先减左右儿子的入度,再入队,会加两次,导致错误。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=1000010;
    int ind[maxn],ans[maxn];
    int a[maxn],b[maxn],q[maxn],head,tail;
    ll Now[maxn],N; char c[maxn][3];
    int main()
    {
        int M,u,v;
        scanf("%lld%d",&N,&M);
        rep(i,1,M){
            scanf("%s%d%d",c[i],&a[i],&b[i]);
            ind[a[i]]++; ind[b[i]]++;
        }
        Now[1]=N; rep(i,1,M) if(!ind[i]) q[++head]=i;
        while(tail<head){
            u=q[++tail];
            if(u==0) break;
            if(Now[u]%2==1) ans[u]=1;
            if(c[u][0]=='L'){
                Now[a[u]]+=(Now[u]-Now[u]/2);
                Now[b[u]]+=Now[u]/2;
            }
            else {
                Now[b[u]]+=(Now[u]-Now[u]/2);
                Now[a[u]]+=Now[u]/2;
            }
            ind[a[u]]--;
            if(ind[a[u]]==0) q[++head]=a[u];
            ind[b[u]]--;
            if(ind[b[u]]==0) q[++head]=b[u];
        }
        rep(i,1,M) {
            if(ans[i]) {
                if(c[i][0]=='L') putchar('R');
                else putchar('L');
            }
            else {
                if(c[i][0]=='R') putchar('R');
                else putchar('L');
            }
        }
        return 0;
    }

    E :Euclidean TSP

    pro:虽然题目是TSP,但是和TSP没什么大关系。 就是给定飞机上升的时间和直线飞行的时间,二者和C的关系,求最小时间。

    sol:飞机飞高需要时间,飞高之后飞得快,不难想到总时间随高度先增后减,三分即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    double n, p, s, v;
    const double eps = 1e9;
    const double t = sqrt(2.0);
    double f(double x)
    {
        double tmp = n * pow(log(n) / log(2.0), t * x);
        tmp = tmp /(eps * p);
        tmp = tmp + s / v * (x + 1.0) / x;
        return tmp;
    }
    int main(){
        cin >> n >> p >> s >> v;
        double l =0, r = 100, ans=1e60, c;
        int T = 100;
        while(T--)
        {
            double lmid = l + (r - l) / 3.0;
            double rmid = r - (r - l) / 3.0;
            double Lans=f(lmid),Rans=f(rmid);
            if(Rans> Lans){
                r=rmid;
                if(ans>Lans) ans =Lans, c = lmid;
            }
            else{
                l = lmid;
                if(ans>Rans) ans =Rans, c = rmid;
            }
        }
        printf("%.9f %.9f
    ", ans, c);
        return 0;
    }

    F .Finding Lines

    pro:给定N个点,P; 问是否有超过P%的点在一条直线上。

    sol:随机两个点,看多少点在直线上。(上海大都会原题)

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 10;
    struct node
    {
        ll x, y;
    }c[maxn];
    node a, b;
    bool judge(node c)
    {
        return (b.y - a.y) * (c.x - b.x) == (b.x - a.x) * (c.y - b.y);
    }
    int main()
    {
        srand(time(0));
        int n, p;
        cin >> n >> p;
        for(int i = 1; i <= n; i++)cin >> c[i].x >> c[i].y;
        if(n <= 2)
        {
            cout<<"possible
    ";
            return 0;
        }
        int T = 1000;
        bool flag = 0;
        while(T--)
        {
            int x = (ll)rand() * rand() % n + 1;
            int y = (ll)rand() * rand() % n + 1;
            if(x == y)continue;
            a = c[x], b= c[y];
            int tot = 0;
            for(int i = 1; i <= n; i++)if(judge(c[i]))tot++;
            if(1.0 * tot / n + 1e-9 >= 0.01 * p){flag = 1;break;}
        }
        if(flag)cout<<"possible"<<endl;
        else cout<<"impossible"<<endl;
        return 0;
    }

    G .Gathering

    pro:给定二维平面上N个点,以及D。要求找一个点X,满足X到N个点的曼哈顿距离都不超过D,而总距离最小。 不存在合法的输出"impossible"

    sol:因为我们知道中位数这个位置比较关键,X和Y可以分开考虑,分别是取中位数时最小,对X和Y都是凹函数。

    比赛的时候大方向想对了。大概是曼哈顿距离转化切比雪夫,然后套二分或者三分。 (但是居然求公共矩形不会了。。。赛后想不就是对R和U取min,对取L和U取max吗。。。 然后我们在合法的多边形里三分套三分即可。 也可以三分之后直接找最接近中位数的Y即可。

    曼哈顿转切比雪夫:  原点A(X,Y)---->逆时针旋转45度后,长度*sqrt2的点A‘(X+Y,X-Y);

    切比雪夫转曼哈顿:  L<=x<=R ; D<=y<=U ; 现在:(L+D)/2<=x<=(R+U)/2,且x一定时满足,L<=x+y<=R,D<=y<=U;

    三分套三分:O(N(log1e9)^2~=9e7),会超时T37.

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=100010;
    const ll inf=1e30;
    ll x[maxn],y[maxn],D,ans=1e30,midy;
    ll up=inf,dn=-inf,le=-inf,ri=inf; int N;
    ll solvey(ll midx,ll Y)
    {
        ll res=0;
        rep(i,1,N) res+=abs(midx-x[i])+abs(Y-y[i]);
        return res;
    }
    ll solvex(ll midx)
    {
        ll yu=min(midx-dn,ri-midx);
        ll yd=max(midx-up,le-midx);
        if(yu<yd) return inf;
        ll res=inf,mid;
        while(yd<yu-2){
            ll l=yd+(yu-yd)/3,ans1=solvey(midx,l);
            ll r=yu-(yu-yd)/3,ans2=solvey(midx,r);
            if(ans1>ans2) yd=l,res=min(res,ans2);
            else yu=r,res=min(res,ans1);
        }
        rep(i,yd,yu) res=min(res,solvey(midx,i));
        return res;
    }
    int main()
    {
        scanf("%d",&N);
        rep(i,1,N) scanf("%lld%lld",&x[i],&y[i]);
        scanf("%lld",&D);
        rep(i,1,N){
            dn=max(dn,x[i]-y[i]-D);
            up=min(up,x[i]-y[i]+D);
            le=max(le,x[i]+y[i]-D);
            ri=min(ri,x[i]+y[i]+D);
        }
        if(dn>up||le>ri) return puts("impossible"),0;
        sort(y+1,y+N+1); midy=(y[(N+1)/2]);
        ll L=(le+dn)/2,R=(up+ri)/2;
        while(L<R-2){
            ll l=L+(R-L)/3,ans1=solvex(l);
            ll r=R-(R-L)/3,ans2=solvex(r);
            if(ans1>ans2) L=l,ans=min(ans,ans2);
            else R=r,ans=min(ans,ans1);
        }
        rep(i,L,R) ans=min(ans,solvex(i));
        printf("%lld
    ",ans);
        return 0;
    }

    三分+中位数逼近:没必要取三分y,直接用结论即可,取靠近y[]的中位数。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=100010;
    const ll inf=1e30;
    ll x[maxn],y[maxn],D,ans=1e30,midy;
    ll up=inf,dn=-inf,le=-inf,ri=inf; int N;
    ll solve(ll midx)
    {
        ll yu=min(midx-dn,ri-midx);
        ll yd=max(midx-up,le-midx);
        if(yu<yd) return inf;
        ll Y,res=0;
        rep(i,1,N) res+=abs(midx-x[i])+abs(Y-y[i]);
        return res;
    }
    int main()
    {
        scanf("%d",&N);
        rep(i,1,N) scanf("%lld%lld",&x[i],&y[i]);
        scanf("%lld",&D);
        rep(i,1,N){
            dn=max(dn,x[i]-y[i]-D);
            up=min(up,x[i]-y[i]+D);
            le=max(le,x[i]+y[i]-D);
            ri=min(ri,x[i]+y[i]+D);
        }
        if(dn>up||le>ri) return puts("impossible"),0;
        sort(y+1,y+N+1); midy=(y[(N+1)/2]);
        ll L=(le+dn)/2,R=(up+ri)/2;
        while(L<R-2){
            ll l=L+(R-L)/3,ans1=solve(l);
            ll r=R-(R-L)/3,ans2=solve(r);
            if(ans1>ans2) L=l,ans=min(ans,ans2);
            else R=r,ans=min(ans,ans1);
        }
        rep(i,L,R) ans=min(ans,solve(i));
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    H .Hyacinth

    pro:看不懂。

    sol:by队友

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    struct edge{
        int d,nex;
    }e[20010];
    struct node{
        int x,y;
        bool a,b;
    }a[10010];
    int q[10010],efree,n,cnt;
    inline void add(int x,int y){e[++efree]=(edge){y,q[x]},q[x]=efree;}
    void dfs(int x,int y){
        if(a[x].a==0)a[x].x=++cnt;
        if(a[x].b==0)a[x].y=++cnt;
        int v=q[x];while(e[v].d==y)v=e[v].nex;
        if(!v)return;
        if(a[x].a)a[e[v].d].x=a[x].y,a[e[v].d].a=a[x].b=1;
        else a[e[v].d].x=a[x].x,a[e[v].d].a=a[x].a=1;
        dfs(e[v].d,x);
        for(int i=e[v].nex;i;i=e[i].nex)
            if(e[i].d!=y){
                if(a[x].a)a[e[i].d].x=a[x].y,a[e[i].d].a=a[x].b=1;
                else a[e[i].d].x=a[x].x,a[e[i].d].a=a[x].a=1;
                dfs(e[i].d,x);
            }
    }
    int main(){
        scanf("%d",&n);
        if(n==2){
            puts("1 2");
            puts("2 1");
            return 0;
        }
        for(int i=1;i<n;i++){
            int x,y;scanf("%d%d",&x,&y);
            add(x,y),add(y,x);
        }
        dfs(1,0);
        for(int i=1;i<=n;i++)printf("%d %d
    ",a[i].x,a[i].y);
        return 0;
    }

    I. Indoorienteering

    题意:找长度为L的哈密尔顿回路,N<=14;

    思路:N!太大,我们可以折半枚举

    J .Judging Troubles

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=2000010;
    map<string, int>Map1, Map2;
    int main()
    {
        int n, ans = 0;
        string s;
        cin >> n;
        for(int i = 1; i <= n; i++)cin >> s, Map1[s]++;
        for(int i = 1; i <= n; i++)cin >> s, Map2[s]++;
        for(map<string, int>::iterator it = Map1.begin(); it != Map1.end(); it++)
            ans += min(Map1[it->first], Map2[it->first]);
        cout<<ans<<endl;
        return 0;
    }

    Knapsack Collection

    队友写了,但是代码没给我啊。。。

  • 相关阅读:
    纪念这一天,我找到了实习工作
    在编程的道路上坚定地走下去
    今天是1024程序员节
    趣谈函数调用与返回值
    为期3个月的实训结束了,有感而发
    学习编程时遇到难点怎么办?
    今天学习了安卓中的UI线程
    Java程序员要掌握的常用快捷键
    我是一个注重基础的人
    我也有自己的小家啦
  • 原文地址:https://www.cnblogs.com/hua-dong/p/10500513.html
Copyright © 2011-2022 走看看