zoukankan      html  css  js  c++  java
  • 国庆 day 1 下午

    一道图论好题(graph)

    Time Limit:1000ms   Memory Limit:128MB

     

    题目描述

    LYK有一张无向图G={V,E},这张无向图有n个点m条边组成。并且这是一张带权图,不仅有边权还有点权。

    LYK给出了一个子图的定义,一张图G’={V’,E’}被称作G的子图,当且仅当

    ·G’的点集V’包含于G的点集V。

    ·对于E中的任意两个点a,b∈V’,当(a,b)∈E时,(a,b)一定也属于E’,并且连接这两个点的边的边权是一样的。

    LYK给一个子图定义了它的价值,它的价值为:点权之和与边权之和的比。 看

    LYK想找到一个价值最大的非空子图,所以它来找你帮忙啦。

     

    输入格式(graph.in)

    第一行两个数n,m表示一张n个点m条边的图。

    第二行n个数ai表示点权。

    接下来m行每行三个数u,v,z,表示有一条连接u,v的边权为z的无向边。数据保证任意两个点之间最多一条边相连,并且不存在自环。

     

    输出格式(graph.out)

    你需要输出这个价值最大的非空子图的价值,由于它是一个浮点数,你只需要保留小数点后两位有效数字。

     

    输入样例

    3 3

    2 3 4

    1 2 3

    1 3 4

    2 3 5

     

    输出样例

    1.67

     

    样例解释

    选择1,2两个点,则价值为5/3=1.67。

     

    对于20%的数据n=2

    对于50%的数据n<=5

    对于100%的数据1<=n,m<=1000001<=ai,z<=1000。

    思路:当时跑了01分数规划,但是用的bfs找的负环,所以TLE,卡了我5个点。

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 100010
    using namespace std;
    int n,m,tot;
    double w[MAXN],dis[MAXN];
    double l=0,r=1500,ans,mid;
    int val[MAXN],vis[MAXN],num[MAXN];
    int to[MAXN*2],net[MAXN*2],cap[MAXN*2],head[MAXN*2];
    void add(int u,int v,int w){
        to[++tot]=v;net[tot]=head[u];cap[tot]=w;head[u]=tot;
        to[++tot]=u;net[tot]=head[v];cap[tot]=w;head[v]=tot;
    }
    bool spfa(int s){
        queue<int>que;
        memset(num,0,sizeof(num));
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        que.push(s);
        dis[s]=0;vis[s]=1;num[s]++;
        while(!que.empty()){
            int now=que.front();
            que.pop();
            vis[now]=0;
            for(int i=head[now];i;i=net[i]){
                if(dis[to[i]]>dis[now]+w[i]){
                    dis[to[i]]=dis[now]+w[i];
                    if(!vis[to[i]]){
                        vis[to[i]]=1;
                        num[to[i]]++;
                        que.push(to[i]);
                        if(num[to[i]]>n)    return true;
                    }
                }
            }
        }
        return false;
    }
    void work(){
        for(int i=1;i<=tot;i++)
            w[i]=(double)cap[i]*mid-val[to[i]];
    }
    bool check(){
        for(int i=1;i<=n;i++)
            if(spfa(i))    return true;
        return false;
    }
    int main(){
        freopen("graph.in","r",stdin);
        freopen("graph.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)    scanf("%d",&val[i]);
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        while(r-l>0.0000001){
            mid=(l+r)/2;
            work();
            if(check()){
                ans=mid;
                l=mid;
            }
            else r=mid;
        }
        printf("%.2lf",ans*2);
    }
    View Code

    然后改成dfs找负环,AC了。

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const double eps=1e-5;
    const int mxn=100010;
    int read(){
        int x=0,f=1;char ch=getchar();
        while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    struct edge{
        int v,nxt,w;
        double c;
    }e[mxn<<1];
    int hd[mxn*2],mct=0;
    void add_edge(int u,int v,int w){
        e[++mct].v=v;e[mct].nxt=hd[u];hd[u]=mct;e[mct].w=w;return;
    }
    bool vis[mxn];
    double dis[mxn];
    bool SPFA(int u){
        vis[u]=1;
        for(int i=hd[u];i;i=e[i].nxt){
            int v=e[i].v;
            if(dis[v]>dis[u]+e[i].c){
                dis[v]=dis[u]+e[i].c;
                if(vis[v] || SPFA(v)){
                    vis[v]=0;return 1;
                }
            }
        }
        vis[u]=0;
        return 0;
    }
    int n,m;
    int f[mxn];
    void restore(double r){
        for(int i=1;i<=mct;i++)
            e[i].c=(double)e[i].w*r-f[e[i].v];
        return;
    }
    bool check(){
        for(int i=1;i<=n;i++)
            if(SPFA(i))return 1;
        return 0;
    }
    int main(){
        freopen("graph.in","r",stdin);
        freopen("graph.out","w",stdout);
        int i,j;
        int u,v,w;
        n=read();m=read();
        for(i=1;i<=n;i++)
            f[i]=read();
        for(i=1;i<=m;i++){
            u=read();v=read();w=read();
            add_edge(u,v,w);
            add_edge(v,u,w);
        }
        double l=0,r=1500,ans;
        while(r-l>eps){
            double mid=(l+r)/2;
            restore(mid);
            if(check()){
                ans=mid; 
                l=mid;
            }else r=mid;
        }
        printf("%.2f
    ",ans*2);
        return 0;
    }
    View Code

    后来听老师讲了之后发现这是道结论题:最优解一定是一条边+两个点。

    #include <cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    double ans;
    int A,B,C,n,m;
    int a[100005];
    int main(){
        freopen("graph.in","r",stdin);
        freopen("graph.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&A,&B,&C);
            ans=max(ans,(a[A]+a[B])/(C+0.0));
        }
        printf("%.2f
    ",ans);
        return 0;
    }
    View Code

    拍照(photo)

    Time Limit:1000ms   Memory Limit:128MB

     

    题目描述

    假设这是一个二次元。

    LYK召集了n个小伙伴一起来拍照。他们分别有自己的身高Hi和宽度Wi。

    为了放下这个照片并且每个小伙伴都完整的露出来,必须需要一个宽度为ΣWi,长度为max{Hi}的相框。(因为不能叠罗汉)。

    LYK为了节省相框的空间,它有了绝妙的idea,让部分人躺着!一个人躺着相当于是身高变成了Wi,宽度变成了Hi。但是很多人躺着不好看,于是LYK规定最多只有n/2个人躺着。(也就是说当n=3时最多只有1个人躺着,当n=4时最多只有2个人躺着)

    LYK现在想问你,当其中部分人躺着后,相框的面积最少是多少。

     

    输入格式(photo.in)

    第一行一个数n。

        接下来n行,每行两个数分别是Wi,Hi

     

    输出格式(photo.out)

    你需要输出这个相框的面积最少是多少。

     

    输入样例

    3

    3 1

    2 2

    4 3

     

    输出样例

    27

     

    样例解释

    如果没人躺过来,需要27的面积。

    我们只要让第1个人躺过来,就只需要21的面积!

     

    对于30%的数据n<=10。

    对于60%的数据n<=1000Wi,Hi<=10。

    对于100%的数据1<=n,Wi,Hi<=1000。

     思路:考试的时候写了个DP,但是忘了考虑他的后效性,所以挂掉了8个点。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,sumh,sumw;
    int ans=0x7f7f7f7f;
    int w[1001],h[1001];
    int f[1001][501],sum[1001][501];
    int maxh[1001][501],maxw[1001][501];
    int main(){
        freopen("photo.in","r",stdin);
        freopen("photo.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&w[i],&h[i]);
            sumw+=w[i];
            sumh+=h[i];
            maxh[i][i]=max(w[i],maxh[i-1][i-1]);
            maxh[i][0]=max(h[i],maxh[i-1][0]);
            f[i][0]=maxh[i][0]*sumw;
            f[i][i]=maxh[i][i]*sumh;
            sum[i][0]=sumw;
            sum[i][i]=sumh;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<i;j++){
                int bns=0;
                f[i][j]=f[i-1][j];
                if(h[i]<=maxh[i-1][j]){
                    f[i][j]+=w[i]*maxh[i-1][j];
                    maxh[i][j]=maxh[i-1][j];
                }    
                else if(h[i]>maxh[i-1][j]){
                    f[i][j]+=sum[i-1][j]*(h[i]-maxh[i-1][j])+w[i]*h[i];
                    maxh[i][j]=h[i];
                }
                sum[i][j]=sum[i-1][j]+w[i];
                
                if(w[i]<=maxh[i-1][j-1])    bns+=h[i]*maxh[i-1][j-1];
                else if(w[i]>maxh[i-1][j-1])    bns+=sum[i-1][j-1]*(w[i]-maxh[i-1][j-1])+w[i]*h[i];
                if(f[i][j]>f[i-1][j-1]+bns){
                    f[i][j]=f[i-1][j-1]+bns;
                    maxh[i][j]=max(w[i],maxh[i-1][j-1]);
                    sum[i][j]=sum[i-1][j-1]+h[i];
                }
            }
        for(int i=0;i<=n/2;i++)
            ans=min(f[n][i],ans);
        cout<<ans;
    }
    View Code

    正解思路:枚举高度,贪心累计宽度

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 1005
    using namespace std;
    int n,ans=0x7f7f7f7f;
    int w[MAXN],h[MAXN],bns[MAXN];
    int cmp(int a,int b){
        return a>b;
    }
    int main(){
        freopen("photo.in","r",stdin);
        freopen("photo.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&w[i],&h[i]);
        for(int i=1;i<=1000;i++){
            int sum=0,flag=0,cnt=0,num=0;
            for(int j=1;j<=n;j++)
                if(h[j]<=i&&(w[j]<h[j]||w[j]>i))
                    sum+=w[j];
                else if(w[j]>i&&h[j]>i){
                    flag=1;
                    break;
                }
                else if(h[j]>i){
                    cnt++;
                    sum+=h[j];
                }
                else{
                    bns[++num]=w[j]-h[j];
                    sum+=w[j];
                }
            if(flag)    continue;
            if(cnt>n/2)    continue;
            sort(bns+1,bns+1+num,cmp);
            for(int j=1;j<=min(n/2-cnt,num);j++)
                sum-=bns[j];
            ans=min(ans,sum*i);
        }
        cout<<ans;
    }
    View Code

    或和异或(xor)

    Time Limit:2000ms   Memory Limit:128MB

     

    题目描述

    LYK最近在研究位运算,它研究的主要有两个:or和xor。(C语言中对于|和^)

    为了更好的了解这两个运算符,LYK找来了一个2^n长度的数组。它第一次先对所有相邻两个数执行or操作,得到一个2^(n-1)长度的数组。也就是说,如果一开始时a[1],a[2],…,a[2^n],执行完第一次操作后,会得到a[1] or a[2],a[3] or a[4] ,…, a[(2^n)-1] or a[2^n]。

    第二次操作,LYK会将所有相邻两个数执行xor操作,得到一个2^(n-2)长度的数组,假如第一次操作后的数组是b[1],b[2],…,b[2^(n-1)],那么执行完这次操作后会变成b[1] xor b[2], b[3] xor b[4] ,…, b[(2^(n-1))-1] xor b[2^(n-1)]。

    第三次操作,LYK仍然将执行or操作,第四次LYK执行xor操作。如此交替进行。

    最终这2^n个数一定会变成1个数。LYK想知道最终这个数是多少。

    为了让这个游戏更好玩,LYK还会执行Q次修改操作。每次修改原先的2^n长度的数组中的某一个数,对于每次修改操作,你需要输出n次操作后(最后一定只剩下唯一一个数)剩下的那个数是多少。

     

    输入格式(xor.in)

    第一行两个数n,Q。

    接下来一行2^n个数ai表示一开始的数组。

    接下来Q行,每行两个数xi,yi,表示LYK这次的修改操作是将a{xi}改成yi。

     

    输出格式(xor.out)

    Q行,表示每次修改操作后执行n次操作后剩下的那个数的值。

     

    输入样例

    2 4

    1 6 3 5

    1 4

    3 4

    1 2

    1 2

     

    输出样例

    1

    3

    3

    3

     

    样例解释

    第一次修改,{4,6,3,5}->{6,7}->{1}

    第二次修改,{4,6,4,5}->{6,5}->{3}

    第三次修改,{2,6,4,5}->{6,5}->{3}

    第四次修改,{2,6,4,5}->{6,5}->{3}

    对于30%的数据n<=17Q=1。

    对于另外20%的数据n<=10,Q<=1000。

    对于再另外30%的数据n<=12,Q<=100000。

    对于100%的数据1<=n<=171<=Q<=10^51<=xi<=2^n,0<=yi<2^300<=ai<2^30。

    思路:倍增。

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,m;
    bool ok;
    int a[21][200001];
    int poss,v;
    int main(){
        freopen("xor.in","r",stdin);
        freopen("xor.out","w",stdout);
        scanf("%d%d",&n,&m);
        int len=pow(2,n);
        for(int i=1;i<=len;i++) scanf("%d",&a[n][i]);
        for(int i=n-1;i>=0;i--){
            int l=pow(2,i);
            if((n-i)%2)
                for(int j=1;j<=l;j++)
                    a[i][j]=a[i+1][(j<<1)-1]|a[i+1][(j<<1)];
            else
                for(int j=1;j<=l;j++)
                    a[i][j]=a[i+1][(j<<1)-1]^a[i+1][(j<<1)];
        }
        for(int z=1;z<=m;z++){
            ok=false;int val,pos;
            scanf("%d%d",&pos,&val);
            a[n][pos]=val;
            if(pos%2) poss=pos+1;
            else{
                poss=pos;
                pos--;
            }
            for(int i=n-1;i>=0;i--)
                if((n-i)%2){
                    v=a[i+1][pos]|a[i+1][poss];
                    if(v==a[i][(pos>>1)+(pos-((pos>>1)<<1))]){
                        printf("%d
    ",a[0][1]);
                        ok=true;
                        break;
                    }
                    a[i][(pos>>1)+(pos-((pos>>1)<<1))]=v;
                    pos=(pos>>1)+(pos-((pos>>1)<<1));
                    if(pos%2) poss=pos+1;
                    else{
                        poss=pos;
                        pos--;
                    }
                }
                else{
                    v=a[i+1][pos]^a[i+1][poss];
                    if(v==a[i][(pos>>1)+(pos-((pos>>1)<<1))]){
                        printf("%d
    ",a[0][1]);
                        ok=true;
                        break;
                    }
                    a[i][(pos>>1)+(pos-((pos>>1)<<1))]=v;
                    pos=(pos>>1)+(pos-((pos>>1)<<1));
                    if(pos%2) poss=pos+1;
                    else{
                        poss=pos;
                        pos--;
                    }
                }
            if(!ok) printf("%d
    ",a[0][1]);
        }
    }
    View Code

    另一种思路:线段树。

    #include<cstdio>
    #include<iostream>
    #define N 131073
    using namespace std;
    int n,m;
    int sum[N<<2],mid[N<<2];
    void build(int now,int l,int r,int dep){
        if(l==r){
            scanf("%d",&sum[now]);
            return;
        }
        mid[now]=(l+r)/2;
        build(now*2,l,mid[now],dep+1);
        build(now*2+1,mid[now]+1,r,dep+1);
        if((n-dep+1)&1) sum[now]=(sum[now*2]|sum[now*2+1]);
        else sum[now]=(sum[now*2]^sum[now*2+1]);
    }
    void change(int now,int l,int r,int pos,int w,int dep){
        if(l==r){
            sum[now]=w;
            return;
        }
        if(pos<=mid[now]) change(now*2,l,mid[now],pos,w,dep+1);
        else change(now*2+1,mid[now]+1,r,pos,w,dep+1);
        if((n-dep+1)&1) sum[now]=(sum[now*2]|sum[now*2+1]);
        else sum[now]=(sum[now*2]^sum[now*2+1]);
    }
    int main(){
        freopen("xor.in","r",stdin);
        freopen("xor.out","w",stdout);
        scanf("%d%d",&n,&m);
        int s=1<<n;
        build(1,1,s,1);
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            change(1,1,s,x,y,1);
            printf("%d
    ",sum[1]);
        }
    }
    View Code
    细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
  • 相关阅读:
    跳转练习
    从入门到自闭之Python--Redis
    从入门到自闭之Python--Django Rest_Framework
    从入门到自闭之Python--RESTful API规范与序列化
    从入门到自闭之Python--虚拟环境如何安装
    从入门到自闭之Python集合,深浅拷贝(大坑)
    从入门到自闭之Python编码
    从入门到自闭之Python字典如何使用
    从入门到自闭之Python列表,元祖及range
    从入门到自闭之Python整型,字符串以及for循环
  • 原文地址:https://www.cnblogs.com/cangT-Tlan/p/7639603.html
Copyright © 2011-2022 走看看