atcoder reguler contest 089 E graph xy

构造一个满足下列条件的有向图:
1. 顶点数不超过 300 ,其中有一个源点 S 和一个汇点 T
2. 没有重边和自环
3. 顶点由 1N 编号
4. 每条边的权值 [0,100] ,也可以是一个标签 XY
5. 对于给定的每一对满足 x[1,A],y[1,B](x,y) ,在标签为 X 的边的权值为 x ,标签 Y 的权值为 y 时,源点 S 到汇点 T 的最短路径长度为 d(x,y)

1A,B10
1d(x,y)100


每一条最短路都经过了一些 X 边,一些 Y 边和一些常数边。我们发现如果有两条 ST 的路径包含了相同多的 X 边和 Y 边,常数边大的那条边是没有意义的,因为对于所有 (x,y) ,常数项小的那条路径的权值肯定比常数项大的小。

d(i,j)100 ,所以我们可以按如下方式构图:
这里写图片描述

下半部分顶点可以自由向上半部分连常数权值的边。
我们设 f(i,j) 代表过了 iX 边, jY 边的常数边的权值,例如 f(1,2) 就代表了图中 1 号点与 102 号点之间常数边的权值。

那么,

d(i,j)=min{ f(a,b)+ai+bj }

因为我们要确定 f(a,b) 的值,所以我们反过来考虑
f(a,b)d(i,j)aibj,  iA,jB

我们让每一个 f(a,b) 都尽量小,这样就给最小的 d(i,j) 提供了一条最短路。

确定每个f(a,b) 的值后,我们只需要验证一下是否符合即可。

O(d(i,j)2AB)

code:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 350;
    const int INF = 1<<30;

    int a, b, s, t, sum, d[N][N], f[N][N];

    inline void ck_min( int &a, int b ) { if( a > b ) a = b; }
    inline void ck_max( int &a, int b ) { if( a < b ) a = b; }

    inline bool check( int x, int y, int val )
    {
        int res = INF;
        for( int i = 0; i <= 100; i ++ )
            for( int j = 0; j <= 100; j ++ )
                if( f[i][j] >= 0 ) ck_min( res, i*x + j*y + f[i][j] );

    //  printf( "x:%d, y:%d, res:%d\n", x, y, res );
        return res == val;
    }

    int main()
    {
        memset( f, -1, sizeof( f ) );

        scanf( "%d%d", &a, &b );
        for( int i = 1; i <= a; i ++ )
            for( int j = 1; j <= b; j ++ )
                scanf( "%d", &d[i][j] );

        s = 201, t = 202; 
        // s --> 1 --> 2 --> ... --> 100
        // t <-- 101 <-- 102 <-- ... <-- 200

        for( int i = 0; i <= 100; i ++ )
            for( int j = 0; j <= 100; j ++ )
            {
                int tmp = -INF;
                for( int _a = 1; _a <= a; _a ++ )
                    for( int _b = 1; _b <= b; _b ++ )
                        ck_max( tmp, d[_a][_b] - i*_a - j*_b ); 
                f[i][j] = tmp; 
                if( f[i][j] < 0 ) break;

                sum ++;
    //          printf( "i:%d, j:%d, f:%d\n", i, j, f[i][j] );
            }

        for( int i = 1; i <= a; i ++ )
            for( int j = 1; j <= b; j ++ )  
                if( !check( i, j, d[i][j] ) ) {
                    printf( "Impossible\n" ); return 0;
                } 

        printf( "Possible\n" );

        printf( "%d %d\n", 202, sum+200 );
        for( int i = 1; i < 100; i ++ )
            printf( "%d %d X\n%d %d Y\n", i, i+1, i+101, i+100 );

        printf( "%d %d X\n%d %d Y\n", s, 1, 101, t );
        for( int i = 0; i <= 100; i ++ )
            for( int j = 0; j <= 100; j ++ )
                if( f[i][j] >= 0 )  printf( "%d %d %d\n", !i ? s : i, !j ? t : j+100, f[i][j] );

        printf( "%d %d\n", s, t );

        return 0;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章