题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4281
题意:一场比赛要有若干个裁判去回答N个问题,第i个问题在(xi, yi)处,裁判解答该问题需要ci分钟,每个裁判最多能解答M分钟的问题,每个点只能被一个裁判访问,从一点走到另一点的时间为其欧几里得距离值,问:
1.最少需要多少裁判才能回答完所有问题
2.假设有无数个裁判,求回答完所有问题的最少行走时间(题目未说清,裁判最终要回到起点)
思路:可以先预处理出任意状态是否能够满足总解答时间 <= M
对于问题1:可以将权值和 <= M的集合看作一个个背包,dp[s]可以表示达到s状态所需的最小花费,对于没有交集的两个集合则有转移:dp[s | t] = min(dp[s | t], dp[t] + 1)
对于问题2:可以预处理出一个裁判遍历各个状态并回到起点所需的花费,然后对于任一状态,通过枚举子集找到其子集和子集补集的花费,因为裁判最后要回到起点故有转移:dp[s] = min(dp[s], dp[sub | 1] + dp[(s ^ sub) | 1])
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <climits>
#include <functional>
#include <deque>
#include <ctime>
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int inf = 0x3f3f3f3f;
struct node
{
int x, y;
} p[20];
int n, m;
int dp[1 << 17][17], ans1[1 << 17], ans2[1 << 17], ok[1 << 17];
int dis[17][17], v[17];
int dist(int a, int b)
{
return ceil(sqrt((p[a]. x - p[b].x) * (p[a].x - p[b].x) + (p[a].y - p[b].y) * (p[a].y - p[b].y)));
}
bool check(int s)
{
int res = 0;
for (int i = 0; i < n; i++)
{
if (s & (1 << i))
res += v[i];
}
if (res <= m) return true;
return false;
}
void init()
{
for (int i = 0; i < n; i++)
scanf("%d%d", &p[i].x, &p[i].y);
for (int i = 0; i < n; i++)
scanf("%d", &v[i]);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
dis[i][j] = dist(i, j);
for (int i = 0; i < (1 << n); i++)
if (check(i))
ok[i] = 1;
else
ok[i] = 0;
}
int solve1()
{
memset(ans1, -1, sizeof(ans1));
ans1[0] = 0;
for (int s = 0; s < (1 << n); s++)
{
if (ok[s])
{
for (int t = 0; t < (1 << n); t++)
{
if (s & t == 1) continue;
if (ans1[t] == -1) continue;
// printf("%d %d %d\n", s, t, s | t);
if (ans1[s | t] == -1)
ans1[s | t] = ans1[t] + 1;
else
ans1[s | t] = min(ans1[s | t], ans1[t] + 1);
}
}
}
return ans1[(1 << n) - 1];
}
int solve2()
{
memset(dp, -1, sizeof(dp));
memset(ans2, -1, sizeof(ans2));
dp[1][0] = 0;
for (int s = 0; s < (1 << n); s++)
{
if (ok[s])
{
for (int i = 0; i < n; i++)
{
if (s & (1 << i) == 0) continue;
if (dp[s][i] == -1) continue;
for (int j = 0; j < n; j++)
{
if (s & (1 << j) == 1) continue;
int t = s | (1 << j);
//printf("%d %d\n", s, t);
if (ok[t])
{
if (dp[t][j] == -1)
dp[t][j] = dp[s][i] + dis[i][j];
else
dp[t][j] = min(dp[t][j], dp[s][i] + dis[i][j]);
}
}
}
}
}
for (int s = 0; s < (1 << n); s++)
for (int i = 0; i < n; i++)
{
if (dp[s][i] == -1) continue;
if (s & (1 << i) == 0) continue;
if (ans2[s] == -1)
ans2[s] = dp[s][i] + dis[i][0];
else
ans2[s] = min(ans2[s], dp[s][i] + dis[i][0]);
}
for (int s = 0; s < (1 << n); s++)
{
int sub = s;
do
{
if (ans2[sub | 1] == -1 || ans2[(s ^ sub) | 1] == -1)
{
sub = (sub - 1) & s;
continue;
}
if (ans2[s] == -1)
ans2[s] = ans2[sub | 1] + ans2[(s ^ sub) | 1];
else
ans2[s] = min(ans2[s], ans2[sub | 1] + ans2[(s ^ sub) | 1]);
sub = (sub - 1) & s;
} while (sub != s);
}
return ans2[(1 << n) - 1];
}
int main()
{
while (~scanf("%d%d", &n, &m))
{
init();
int res1 = solve1(), res2 = solve2();
printf("%d %d\n", res1, res2);
}
return 0;
}