構造一個滿足下列條件的有向圖:
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;
}