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

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

  • 相关阅读:
    cnblog项目--20190309
    django js引入失效问题
    Python老男孩 day16 函数(六) 匿名函数
    Python老男孩 day16 函数(五) 函数的作用域
    Python老男孩 day15 函数(四) 递归
    Python老男孩 day15 函数(三) 前向引用之'函数即变量'
    Python老男孩 day15 函数(二) 局部变量与全局变量
    Python老男孩 day14 函数(一)
    Python老男孩 day14 字符串格式化
    Python老男孩 day14 集合
  • 原文地址:https://www.cnblogs.com/hua-dong/p/10500513.html
Copyright © 2011-2022 走看看