zoukankan      html  css  js  c++  java
  • 图论2 1010

    最小生成树

    给你一个无向带权连通图,每条边是黑色或白色。让你求一棵恰好有need条白色边的权值和最小的生成树。题目保证有解。

    对于所有数据,V,E<=100000,c为[1,1000]中的正整数。

    题解

    可以知道恰好选到need条白边就是最优的,考虑给所有白边加上一个值,随着值的增大,在生成树中的白边越少,所以可以二分。

    取最后一次满足条件的值。

    排序的时候,对于相同值的边白边优先。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int maxn=100005;
    int n,m,need;
    int fa[maxn],sum,cnt;
    struct edge{
        int x,y,c,val;
        bool operator < (const edge a) const {
          if(val==a.val) return c<a.c;
          return val<a.val;
        }
    }e[maxn],cy[maxn];
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
    
    bool check(int x){
        sum=0,cnt=0;
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++){
            cy[i]=e[i];
            if(!e[i].c) cy[i].val+=x;
        }
        sort(cy+1,cy+m+1);
        for(int i=1;i<=m;i++){
            int dx=find(cy[i].x),dy=find(cy[i].y);
            if(dx!=dy){
                fa[dx]=dy;
                sum+=cy[i].val;
                cnt+=!cy[i].c;
            }
        }
        return cnt>=need;
    }
    
    int main(){
        freopen("e.in","r",stdin);
        freopen("e.out","w",stdout);
        read(n);read(m);read(need);
        for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].val),read(e[i].c);
        int l=-1005,r=1005,ret;
        while(l<=r){
            int mid=(l+r)/2;
            if(check(mid)) ret=sum-need*mid,l=mid+1;//写need 
            else r=mid-1;
        }
        printf("%d",ret);
    }
    e

    很玄的一道题


     

    贪吃蛇

    有两条蛇(1 号蛇和 2 号蛇)在 n 行 m 列的地图上,地图上有障碍物。一条蛇碰到蛇身/障碍物/边界就会死。蛇身会不断长长——可以理解为蛇尾位置不会变,蛇只会向前伸展不会缩尾巴。两条蛇都绝顶聪明,如果自己能赢,一定会尽量快地赢;如果自己会输,一
    定会死得尽量晚。给出初始局面,两蛇轮流走,每次可以且必须向上下左右移动一格。1 号蛇先走,请告诉我谁会在多少回合时赢。

    对于100%的数据,1<=n<=20,1<=m<=20,0的个数不超过50个。

    题解

    用的是alpha-beta算法,还没怎么搞懂,就给个博客

    代码T了两个点

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int inf=1000000;
    const int maxn=25;
    const int oo=400;
    int n,m;
    int mp[maxn][maxn];
    int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
    bool vis[maxn][maxn];
    
    int maxmin(int step,int x1,int y1,int x2,int y2,int alpha,int beta){
        bool opt=false;
        if(step&1){//提高下界 
            for(int i=0;i<4;i++){
                int xx=x1+dx[i],yy=y1+dy[i],val=alpha;
                if(xx>0&&xx<=n&&yy>0&&yy<=m&&!vis[xx][yy]){
                    opt=true;
                    vis[xx][yy]=true;
                    val=maxmin(step+1,xx,yy,x2,y2,alpha,beta);
                    vis[xx][yy]=false;
                }
                if(val>alpha) alpha=val;
                if(alpha>beta) return alpha;
            }
            if(opt) return alpha;
        }
        else {//缩小上界 
            for(int i=0;i<4;i++){
                int xx=x2+dx[i],yy=y2+dy[i],val=beta;
                if(xx>0&&xx<=n&&yy>0&&yy<=m&&!vis[xx][yy]){
                    opt=true;
                    vis[xx][yy]=true;
                    val=maxmin(step+1,x1,y1,xx,yy,alpha,beta);
                    vis[xx][yy]=false;
                }
                if(val<beta) beta=val;
                if(alpha>beta) return beta;
            }
            if(opt) return beta;
        }
        if(step&1) return step-oo;
        return oo-step;
    }
    
    int main(){
        freopen("h.in","r",stdin);
        freopen("h.out","w",stdout);
        int x1,y1,x2,y2;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++){
              scanf("%d",&mp[i][j]);
              if(mp[i][j]==1) {x1=i,y1=j;}
              else if(mp[i][j]==2) {x2=i,y2=j;}
              vis[i][j]=mp[i][j];
         }
        int cx=maxmin(1,x1,y1,x2,y2,-inf,inf);
        if(cx>0) printf("1 %d",oo-cx);
        else printf("2 %d",cx+oo);
    }
    h

    信息拦截

     Alice和Bob经常利用网络传播一些不法信息。网络由n台电脑组成,Alice拥有1号电脑,Bob拥有n号电脑。有m对电脑间可以通过电缆单向传输信息。为了不被拦截,Alice每次传送信息都可以选取任意一条路径(经过的电脑可以重复),把信息沿着电缆传送给Bob。现在政府获知了这一情报,他们可以侵入恰好1台电脑,并获取经过这条电脑的所有信息。政府希望(无论Alice选择的路线如何)他们对Alice发送的每条信息都恰好获取1次(如果太少,会漏掉重要情报;如果太多,他们的电脑会被塞满的)。现在你需要求出政府可以选择入侵哪些电脑以达到要求。

    对于100%的数据,2<=n<=500000,0<=m<=1000000,t<=10。

    可能有自环

    题解

    分析一下题目要求:选取一个点满足 不属于一个点数>1的强联通分量切没有自环(使得信息不多),是1到n的路径的必经点(使得信息不漏

    那么就可以理出做题步骤:1.tarjan缩点  2.判必经点。

    因为不知道有向图的割点能不能用tarjan求,所以就换了《算法竞赛》上的方法:对于有向无环图,求出fs[x]起点到x的路径条数,ft[x]终点到x的路径条数,根据乘法原理:如果fs[x]*ft[x]=fs[t]那么x就是s到t的必经点。

    但是路径条数增长很快,所以考虑使用hash的思想,对路径条数取模,可能会误判。

    求fs和ft可以用拓扑排序来做,但是考虑不能到达n的点对整个图是有影响的,所以建新图的时候只能把能到达n的点放进去。

    #include<queue>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=500005;
    const int maxm=1000005;
    const int mod=1000000007;
    int t,n,m;
    int cnt,head[maxn];
    int cur,low[maxn],dfn[maxn];
    int top,num,s[maxn],res[maxn],mp[maxn];
    bool in[maxn],flag[maxn],circle[maxn];
    ll fs[maxn],ft[maxn];
    struct edge{
        int x,y,next;
    }e[maxm],go[maxm],back[maxm];
    int cntg,cntb,headg[maxn],headb[maxn];
    int g[maxn],b[maxn];
    
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    void add(int x,int y){
        e[++cnt]=(edge){x,y,head[x]};
        head[x]=cnt;
    }
    
    void addg(int x,int y){
        go[++cntg]=(edge){x,y,headg[x]};
        headg[x]=cntg;
    }
    
    void addb(int x,int y){
        back[++cntb]=(edge){x,y,headb[x]};
        headb[x]=cntb;
    }
    
    void init(){
        cnt=num=cntg=cntb=cur=top=0;
        memset(g,0,sizeof(g));
        memset(b,0,sizeof(b));
        memset(fs,0,sizeof(fs));
        memset(ft,0,sizeof(ft));
        memset(mp,0,sizeof(mp));
        memset(res,0,sizeof(res));
        memset(dfn,0,sizeof(dfn));
        memset(head,0,sizeof(head));
        memset(in,false,sizeof(in));
        memset(headg,0,sizeof(headg));
        memset(headb,0,sizeof(headb));
        memset(circle,false,sizeof(circle));
    }
    
    void tarjan(int x){
        if(x==n) in[x]=true;
        dfn[x]=low[x]=++cur;
        s[++top]=x;flag[x]=true;
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            if(!dfn[y]) {
                tarjan(y);
                low[x]=min(low[x],low[y]);
            }
            else if(flag[y]) low[x]=min(low[x],dfn[y]);
            in[x]|=in[y];
        }
        if(dfn[x]==low[x]){
            num++;
            int kk;
            if(s[top]==x) mp[num]=x;
            do{
                kk=s[top--];
                res[kk]=num;
                flag[kk]=false;
            }while(kk!=x);
        }
    }
    
    void back_topsort(){
        queue<int> q;
        ft[res[n]]=1;
        q.push(res[n]);
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=headb[x];i;i=back[i].next){
                int y=back[i].y;
                b[y]--;
                ft[y]+=ft[x];
                if(ft[y]>=mod) ft[y]-=mod;
                if(!b[y]) q.push(y);
            }
        }
    }
    
    void go_topsort(){
        queue<int> q;
        fs[res[1]]=1;
        q.push(res[1]);
        while(!q.empty()){
            int x=q.front();
            q.pop();
            if(mp[x]&&!circle[mp[x]]&&fs[x]*ft[x]%mod==ft[res[1]]) s[++top]=mp[x];
            for(int i=headg[x];i;i=go[i].next){
                int y=go[i].y;
                g[y]--;
                fs[y]+=fs[x];
                if(fs[y]>=mod) fs[y]-=mod;
                if(!g[y]) q.push(y);
            }
        }
    }
    
    void solve(){
        read(n);read(m);
        init();
        for(int i=1;i<=m;i++){
            int x,y;
            read(x);read(y);
            if(x==y) circle[x]=true;
            else add(x,y);
        }
        tarjan(1);
    //    for(int i=1;i<=n;i++) printf("%d ",res[i]);
    //    putchar(10);
        if(!dfn[n]||res[1]==res[n]) {printf("0
    
    ");return ;}//没联通
        for(int x=1;x<=n;x++)
         if(dfn[x]&&in[x]){
              for(int i=head[x];i;i=e[i].next){
                   int y=e[i].y;
                   if(in[y]&&res[x]!=res[y]){
                        addg(res[x],res[y]);g[res[y]]++;
                        addb(res[y],res[x]);b[res[x]]++;
                 }
             }
         }
        back_topsort();
        go_topsort();
        printf("%d
    ",top);
        for(int i=1;i<=top;i++)
         printf("%d ",s[i]);
        putchar(10);
    }
    
    int main(){
        freopen("i.in","r",stdin);
        freopen("i.out","w",stdout);
        read(t);
        while(t--) solve();
    }
    /*
    4
    5 5
    1 3
    4 5
    3 2
    2 4 
    4 2
    */
    i

    std是直接把新图建成无向图跑割点的。

  • 相关阅读:
    获取html
    asp.net上传图片自动生成缩略图功能代码
    又发现了一个好东西zTree,KindEditor,Kissy Editor,脚本安全类工具Burp suite
    C#.Net网络程序开发Socket篇
    简单验证码的制作
    C#:文件创建、复制、移动、删除
    日常提醒2 (delphi源码)
    time_t和SYSTEMTIME 与TDateTime的转换 (转)
    简单的图片对比函数
    胃总是涨 大便不成形 打嗝 口有异味?
  • 原文地址:https://www.cnblogs.com/sto324/p/11656228.html
Copyright © 2011-2022 走看看