构造一个满足下列条件的有向图:
1. 顶点数不超过 ,其中有一个源点 和一个汇点
2. 没有重边和自环
3. 顶点由 至 编号
4. 每条边的权值 ,也可以是一个标签 或
5. 对于给定的每一对满足 的 ,在标签为 的边的权值为 ,标签 的权值为 时,源点 到汇点 的最短路径长度为
每一条最短路都经过了一些 边,一些 边和一些常数边。我们发现如果有两条 至 的路径包含了相同多的 边和 边,常数边大的那条边是没有意义的,因为对于所有 ,常数项小的那条路径的权值肯定比常数项大的小。
且 ,所以我们可以按如下方式构图:
下半部分顶点可以自由向上半部分连常数权值的边。
我们设 代表过了 条 边, 条 边的常数边的权值,例如 就代表了图中 号点与 号点之间常数边的权值。
那么,
因为我们要确定 的值,所以我们反过来考虑
我们让每一个 都尽量小,这样就给最小的 提供了一条最短路。
确定每个 的值后,我们只需要验证一下是否符合即可。
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;
}