ディレクトリ
@説明@
ある日、あなたはNOIゲームに適用することを決めた最大の重みマッチングアルゴリズムを解決するための二部グラフアルゴリズムを学びます。
完全二部グラフを考えます。この図では、二つの部分の大きさはNです。ポイントのUと第一の部分の点Vは、IS右側に接続された第二の部分である\(C_ {} UV UV + K_ {X} \) xは変数の値が定義されていません、。
あなたは、xの値ごとにxの値に複数回与えられます、あなたは最大重みマッチングに対応した完全グラフの総重量の半分に答える必要があります。
入力
数nの最初の行のは、タイトルを定義した通りです。
n個の整数の次のn行、前記CIJの値にi行jの最初の数。
NN整数、次のNN行請求KIJの値にi行jの最初の数。
次に、数qは、xの所定の値の数を表します。
Xの指定された整数値の次Qライン。
出力の
出力Q線、XのI番目の回答値に対応するi番目の行を表す数、各。
サンプル入力
3
0 0 2
0 2 0
2 0 0
0 1 0
1 1 0
0 0 1
3
0
1
3
の出力例
6
7
9
説明
のx = 0、0→2,1→1,2→0の最大マッチング、答えは2 + 2 + 2 = 6です。
X = 1の場合、最大一致のみので、→0依然として0→2,1→1,2である(K_ {11} = 1 \ \) およびKの他の2辺ゼロであり、そして唯一の第二の側縁変更する権利、3。答えは2 + 3 + 2 = 7です。
X = 3の場合、最大のマッチングがk 1の値は、三辺であるので、右側が3に変換され、0,2→2→0→1,1なります。答えは3 + 3 + 3 = 9です。
xの値が与えられたデータの100%、^7,0≤kij≤11≤n≤50,1≤q≤100000,0≤cij≤10、0〜10 ^ 7の間の整数です。
@溶液@
答えは、K * X + Cの形など、いくつかのフォームを見つけることは難しいことではありません 最もN縁一致ため、1 <= K <= N。
さらにユニークな、そのK Cの各々に見出さ これは、変更がNのみの可能性がある答えることを意味します。
私達はちょうどこれらのn個の可能性を発見し、各照会のための課題に対応して可能性を調べる必要があります。
直感的に、Xが増加すると、定数Kが増加する対応する最適解として、(同じ時にも使用することが難しいことを証明するために必要とされる)を求めることができます。
これは、対応するK xの各々は常に連続した範囲であるので、我々はxで2点の値に対応するK間隔の各々を見つけることができることを意味します。
解くためのアルゴリズムKMはO(n ^ 3)と一致する最大重量を解決するための使用を考慮すると、我々はO(N ^ 4 *ログ(の時間複雑さの前処理 A))、 A = 10 ^ 7は一定です。
そしてn質問の可能性を横断するだけなので、O(NQ)の合計クエリ時間複雑。
限り、あなたの文章が本当にKMアルゴリズムであるとして、O(N ^ 3)(#80クイズUOJ行き)の代わりに、O(N ^ 4)生きることができます。
実際には、唯一の本当にXに尋ねた結果のx q回を使用しています。私たちは、ソートクエリー二部に依頼することができ、時間の複雑性Oに近づいすることができる(N ^ 4 *ログ(Q))
しかし、私はこの最適化を書くのが面倒です。
@acceptedコード@
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = 1<<30;
int c[50 + 5][50 + 5], k[50 + 5][50 + 5], K, X, C;
int f(int x, int y) {return k[x][y]*X + c[x][y];}
int lx[50 + 5], ly[50 + 5], lk[50 + 5], slk[50 + 5];
bool vx[50 + 5], vy[50 + 5];
int n, q;
bool dfs(int x) {
vx[x] = true;
for(int y=1;y<=n;y++) {
if( vy[y] ) continue;
int t = lx[x] + ly[y] - f(x, y);
if( t == 0 ) {
vy[y] = true;
if( (!lk[y]) || dfs(lk[y]) ) {
lk[y] = x;
return true;
}
}
else slk[y] = min(slk[y], t);
}
return false;
}
void KM(int p) {
X = p;
for(int i=1;i<=n;i++) {
lx[i] = ly[i] = lk[i] = 0;
for(int j=1;j<=n;j++)
lx[i] = max(lx[i], f(i, j));
}
for(int x=1;x<=n;x++) {
for(int i=1;i<=n;i++)
vx[i] = vy[i] = false, slk[i] = INF;
if( !dfs(x) ) {
while( true ) {
int del = INF, y = 0;
for(int i=1;i<=n;i++)
if( !vy[i] ) del = min(del, slk[i]);
for(int i=1;i<=n;i++) {
if( vx[i] ) lx[i] -= del;
if( vy[i] ) ly[i] += del;
}
for(int i=1;i<=n;i++)
if( !vy[i] ) {
slk[i] -= del;
if( slk[i] == 0 )
y = i;
}
if( !lk[y] ) break;
vx[lk[y]] = vy[y] = true;
for(int i=1;i<=n;i++)
slk[i] = min(slk[i], lx[lk[y]] + ly[i] - f(lk[y], i));
}
for(int i=1;i<=n;i++)
vx[i] = vy[i] = false;
dfs(x);
}
}
K = C = 0;
for(int i=1;i<=n;i++)
K += k[lk[i]][i], C += c[lk[i]][i];
}
struct node{
int k, c, l, r;
node(int _k=0, int _c=0, int _l=0, int _r=0):k(_k), c(_c), l(_l), r(_r){}
};
vector<node>vec;
int pk, pc;
int solve(int l, int r) {
while( l < r ) {
int mid = (l + r + 1) >> 1; KM(mid);
if( pk == K ) l = mid;
else r = mid - 1;
}
return l;
}
int main() {
scanf("%d", &n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d", &c[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d", &k[i][j]);
int le = 0, ri = int(1E7);
while( le <= ri ) {
KM(le); pk = K, pc = C;
int res = solve(le, ri);
vec.push_back(node(pk, pc, le, res));
le = res + 1;
}
scanf("%d", &q);
for(int i=1;i<=q;i++) {
int x; scanf("%d", &x);
for(int j=0;j<vec.size();j++)
if( vec[j].l <= x && x <= vec[j].r )
printf("%d\n", vec[j].k*x + vec[j].c);
}
}
@詳細@
これ自体は問題では難しいことではありません、ただ唯一のn個のK値にK * X + Cの始まりを考える必要があります。
あなたが本当にOを見つけたい(N ^ 3)問題へのコード検索ソリューションは、80 UOJ番号を行くことができます。場合はさらにオンラインKMアルゴリズムのBaiduのうちほとんどは(Baiduの百科事典を含む)偽O(N ^ 3)、あります
実際に、私はKMアルゴリズムがコストに流れることができるかわかりません。費用流の複雑さはO(形而上学)であるが、一方で完全グラフは、より速く実行する必要があります。。。