• uva 515 King


    这个题目,做之前不知道什么事差束约分,看完题目上床睡觉,想了半个小时,想到了要判断一个有向图有没有负环。第二天起来再想,觉得是判断有没有环(包括负环,正环,0环),但是不确定,要真的是这样的话,要权值来干什么,直接拓扑排序就好了,然后不确定,找了解题报告瞄一下,个个都是差束约分,然后没看其他内容,直接去学差束约分,看算法导论和网上的资料看了一个小时,懂了,就写,其实就是差束约分的模板题

     

     说说思路和题目:给你一个长度为n的序列,从下标1到n标号,然后给你m个连续的子序列,让你求和,和要大于或者小于给定的k,如果这m个不等式都能成立,就输出成功,只要有一个不成立就输出不成立。sample中第一个就是成立的,第二个不成立

    然后我们先来设定一个值Xi,Xi=a1+a2+a3……+ai ; 所以输入中是给出一个下标i和长度l,连续的子序列就是 ai+(ai+1)+(ai+2)……(ai+l)

    那么显然就是等于  (Xi+l)-(Xi-1)  (长的连续和减去短的连续和,得到中间的部分),这样我们就构建出了差束约分里面的不等式

    所以可以知道,虽然a序列标号是从1到n,但是X序列显然是从0到n标号的(想想就知道)

    另外一个转化问题就是大于(小于)转化为大于等于(小于等于),因为说了所有数字都是整数所以这个转化很容易的

    Xj-Xi>k   等价于  Xj-Xi >= k+1     Xj-Xi<k  等价于 Xj-Xi <= k-1      (因为都是整数所以可以这样做)

    然后就是差束约分的有向图建图,这里是用了邻接表建图,可以处理掉平行边

    最后一步,设置一个新源点Xn+1  (别忘了是从0到n标号的,所以只能设置n+1),和其余所有出现过的点相连,都是有向边,从Xn+1指向其他点,权值都是0

    所以这样建图后,整个图一定是连通的,在没有负环的情况下一定有可行解,然后就是spfa求最短路,顺便判断负环,有负环就没有可行解,否则就有

    (其实普通的差束约分问题,只要没有负环就有可行解,而且有一组的和就会有无数组)

     

    还有,这道题就是判断有没有可行解,其实就是判断有没有负环(原来就是我昨天晚上想的那样,不过那时候不懂什么是差束约分也证明不了),由于是有向图而且是有负权那当然是spfa,spfa有bfs和dfs版本,两个都写在代码里面,按理论讲,单单判有没有负环的话dfs更好,不过这道题bfs和dfs的时间一样,都跑出了0.072,惊奇的是冲进去了第一名,是我第一次冲进去uva的第一名

    多少要纪念一下啊,毕竟做了uva大半年了第一次冲进第一名(上一次最好成绩是第5名,也是spfa_dfs判负环)

    不过怎么说呢…………这道还是水题,而且能算是模板题吧

    邻接表+spfa判负环

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    #define N 110
    #define M 110
    #define INF 0x3f3f3f3f
    struct edge
    {
        int u,v,w,next;
    }e[M+N];  
    //这个数组不能只开到M,因为后面要加入一个新源点n+1,与0到n的所有顶点连接一条有向边
    int first[N];
    int used[N];  //在输入中出现过哪些点
    int vis[N],c[N];   //spfa_dfs起标记作用
    int d[N];     //最短路径数组
    int n,m,edgenum,s; //s是源点也就是0,edgenum是边集数组的条数
    
    
    void input()
    {
        memset(used,0,sizeof(used));
        memset(first,-1,sizeof(first));
    
        scanf("%d",&m);
        edgenum=0;
        for(int k=0; k<m; k++)  //读入所有不等式,转化为边的信息,但注意边数是edgenum不是k
        {
            int i,j,w;  char op[5];
            scanf("%d%d%s%d",&i,&j,op,&w);
            j+=i; i--; //得到顶点i,j,j标号一定大于i
            used[i]=used[j]=1;  //标记出现过这些点
            if(!strcmp(op,"lt"))  //是小于
            {
                w--;  //相当于<=w-1
                e[edgenum].u=i;
                e[edgenum].v=j;
                e[edgenum].w=w;
                e[edgenum].next=first[i];
                first[i]=edgenum;
                edgenum++;
                
            }
            else     //大于
            {
                w++;  //相当于>=w+1
                e[edgenum].u=j;
                e[edgenum].v=i;
                e[edgenum].w=-w;
                e[edgenum].next=first[j];
                first[j]=edgenum;
                edgenum++;
            }
        }
    
        used[n+1]=1;
        for(int i=0; i<=n; i++) 
            if(used[i])  //新设置一个源点0,跟所有已有的点相连
            {
                e[edgenum].u=n+1;
                e[edgenum].v=i;
                e[edgenum].w=0;
                e[edgenum].next=first[n+1];
                first[n+1]=edgenum;
                edgenum++;
            }
    
        return ;
    }
    
    void print_graph()
    {
        printf("邻接表\n");
        for(int i=0; i<=n+1; i++) 
            if(used[i])
            {
                printf("%d:_____________________\n",i);
                for(int k=first[i]; k!=-1; k=e[k].next)
                    printf("%d\\%d\n",e[k].v,e[k].w);
            }
        printf("*********\n");
        printf("打印边集数组\n");
        for(int i=0; i<edgenum; i++)
            printf("%d  %d  %d  %d\n",e[i].u,e[i].v,e[i].w,e[i].next);
        printf("*********\n");
        return ;
    }
    
    int spfa_dfs(int u)
    {
        vis[u]=1;
        for(int k=first[u]; k!=-1; k=e[k].next)  //遍历顶点u的邻接表
        {
            int v=e[k].v , w=e[k].w;
            if( d[u]+w < d[v])
            {    
                d[v]=d[u]+w;
                if(!vis[v])
                {
                    if(spfa_dfs(v))
                        return 1;
                }
                else return 1;
            }
        }
        vis[u]=0;
        return 0;
    }
    int spfa_bfs(int s)
    {
        int flag=1;
        queue <int> q;
        memset(c,0,sizeof(c)); c[s]++;
        memset(d,0x3f,sizeof(d)); d[s]=0;
        memset(vis,0,sizeof(vis)); vis[s]=1;
        q.push(s);
        while(!q.empty())
        {
            int u=q.front(); vis[u]=0; q.pop();
            for(int k=first[u]; k!=-1; k=e[k].next)
            {
                int v=e[k].v , w=e[k].w;
                if(d[u]+w<d[v])
                {
                    d[v]=d[u]+w;
                    if(!vis[v])
                    {
                        vis[v]=1;
                        q.push(v);
                        c[v]++;
                        if(c[v]>n+1)
                            return 1;  //找到负环
                    }
                }
            }
        }
    
        return 0;  //没有负环
    }
    void judge()
    {
        s=n+1;  //源点
        memset(d,0x3f,sizeof(d)); d[s]=0;
        memset(vis,0,sizeof(vis));
    
        //int tmp=spfa_dfs(s);  
        int tmp=spfa_bfs(s);
        //spfa的dfs版本和bfs版本都有了,都可以AC,注释掉换过来就可以了
        if(tmp)
            printf("successful conspiracy\n");
        else
            printf("lamentable kingdom\n");
    
        return ;
    }
    int main()
    {
        while(scanf("%d",&n) && n)
        {
            input();
            //print_graph();  //测试函数
            judge();  
        }
        return 0;
    }
  • 相关阅读:
    显示保存布局按钮OO ALV(set_table_for_first_display)
    ◆◆0ALV单元格颜色代码
    添加标准状态栏(status)
    添加自定义状态栏(GUI status)
    添加布局(layout)按钮
    添加页眉Top of page和页脚End of Page
    显示设定
    列属性设定-修改列标签名(label),自动优化显示宽度
    cs20_5-1
    cs20_4-2
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2795153.html
Copyright © 2020-2023  润新知