以撒的谜题
Time Limit: 2000/1000 MS (Java/Others) Memory Limit 65536/32768 K (Java/Others)
题目描述
以撒在天堂遇到了最终boss----以撒自己,他需要解开一道谜题来战胜最终的boss。
给出一个初始数列a[],以撒首先将每个a[i]加上[p, q]范围内的一个整数(加的数不一定要相同),然后以撒可以任意调换元素的位置最终得到一个新的数列b[]。现在以撒需要使两个数列对应位置的最大公约数的和值最小,请计算出这个和值得最小值。
输入
首先给出一个正整数T,表示有T组数据。每组数据包括:
第一行为3个整数n,p,q,这里0 < n < 100,0 <= p <= q <= 20;
第二行为n个正整数,表示初始数列,这里0 < a[i] <= 10000。
输出
每组数据输出一个正整数表示和值得最小值
样例输入
2
4 0 0
2 3 6 8
4 1 2
2 3 6 8
样例输出
6
4
题目链接:http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=2094
题目分析:比较裸的二分图最大权匹配,20000条边直接费用流搞了
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int const INF = 0x3fffffff;
int const MAX = 205;
int n, p, q, a[105];
int head[MAX], cnt;
int pre[MAX], dis[MAX];
int src, sk, ans;
bool vis[MAX];
struct EDGE
{
int to, nxt, cap, cost;
}e[MAX * MAX];
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
void Init()
{
ans = 0;
cnt = 0;
memset(head, -1, sizeof(head));
}
void Add(int u, int v, int cap, int cost)
{
e[cnt].to = v;
e[cnt].cap = cap;
e[cnt].cost = cost;
e[cnt].nxt = head[u];
head[u] = cnt ++;
e[cnt].to = u;
e[cnt].cap = 0;
e[cnt].cost = -cost;
e[cnt].nxt = head[v];
head[v] = cnt ++;
}
void Build_graph()
{
src = 0;
sk = 2 * n + 1;
for(int i = 1; i <= n; i++)
Add(src, i, 1, 0);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
int mi = INF;
for(int k = p; k <= q && mi != 1; k++)
mi = min(mi, gcd(a[i], a[j] + k));
Add(i, n + j, 1, mi);
}
}
for(int i = n + 1; i < sk; i++)
Add(i, sk, 1, 0);
}
bool SPFA()
{
memset(vis, false, sizeof(vis));
for(int i = 0; i <= sk; i++)
dis[i] = INF;
queue <int> q;
q.push(src);
vis[src] = true;
dis[src] = 0;
pre[src] = -1;
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = e[i].nxt)
{
int v = e[i].to;
int cost = e[i].cost;
int cap = e[i].cap;
if(cap && dis[v] > dis[u] + cost)
{
dis[v] = dis[u] + cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
return dis[sk] != INF;
}
void Augment()
{
while(SPFA())
{
int mi = INF;
for(int u = sk; u != src && mi != 1; u = e[pre[u] ^ 1].to)
mi = min(mi, e[pre[u]].cap);
for(int u = sk; u != src; u = e[pre[u] ^ 1].to)
{
e[pre[u]].cap -= mi;
e[pre[u] ^ 1].cap += mi;
ans += mi * e[pre[u]].cost;
}
}
}
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
scanf("%d %d %d", &n, &p, &q);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
Init();
Build_graph();
Augment();
printf("%d\n", ans);
}
}