原题链接
题目类型:最优比率环
思考过程:
存在的限制:
1.起始点是不确定的
2.对于某一个地标,第一次到达时具有乐趣,后续不具备乐趣
3.最少要去两个地标
存在的问题:
1.a为乐趣,b为时间,如何将a和b进行对应?存在第二次达到的问题,那么这是一个动态对映的过程吗?
2.如何去找到一条可行的路径(环)?即如何去寻找到一个可行的解?
在这篇题解中,认定了答案是不可能为两个环的交的形式的,但是不知道为什么???
参考题解
假设答案的最终形式一定不会是两个环的交,即一定是一个简单环时,应该如何求解呢?
0/1规划的思路在于二分,为此我们设置L、R和M,并对于M进行检验以更新最终的结果。
那么如何进行检验呢?
假设存在一个环r,使得fun-rcost>0,即L更新为M,否则R更新为M。对于fun-rcost>0,转化为rcost-fun<0,即在边值为边值为rcost-fun的图(每一条边和其进入的点相挂钩)中,是否存在一个负权环?->使用spfa判断即可。
#include
#include
#include
using namespace std;
#define LMAX 1010
#define EPS 1e-6
#define INF 1e9
int L, P;
int fun[LMAX];
vector<vector<pair<int,int > > > E(LMAX);
int spfa(double ans) {
int vis[LMAX];
double dis[LMAX];
int num[LMAX];
for (int i = 1; i <= L; i++) {
vis[i] = 0;
num[i] = 0;
dis[i] = INF;
}
queue<int > Q;
Q.push(1);
vis[1] = 1, dis[1] = 0;
num[1]++;
while (!Q.empty()) {
int u = Q.front();
Q.pop();
vis[u] = 0;
for (int i = 0; i < E[u].size(); i++) {
int v = E[u][i].first;
int w = E[u][i].second;
if (dis[u] + ans * w - fun[v] < dis[v]) {
dis[v] = dis[u] + ans * w - fun[v];
if (!vis[v]) {
Q.push(v);
num[v]++;
if (num[v] == L) return 1;
}
}
}
}
return 0;
}
int main() {
cin >> L >> P;
for (int i = 1; i <= L; i++) scanf_s("%d", &fun[i]);
for (int i = 0; i < P; i++) {
int f, t, w;
scanf_s("%d %d %d", &f, &t, &w);
E[f].push_back(make_pair(t, w));
}
double L = 0, R = 1000, M;
while (R - L > EPS) {
M = (L + R) / 2;
//printf("%.2f %.2f %.2f\n", L, M, R);
if (spfa(M)) L = M;
else R = M;
}
printf("%.2f\n", L);
}
tip:
1.spfa判断环的方式是某个节点的入队次数不应该达到总的点数
原题链接
题目类型:迭代逼近
二分查找针对的是单调函数的定位问题,三分查找针对的则是二次函数的定位问题。可以这样理解,在0/1规划中,使用二分的方式是将L、R为结果数,而此题中的L、R则为方案数,最终通过某个方案得到的解才为真正的结果数。
如何体现覆盖:
已知最大的横坐标差为x_max,最大的纵坐标差为y_max,max=max(x_max,y_max),则必然存在一个边和x、y轴平行的正方型,能够完全覆盖所有的点。
如何体现最小性:
将正方形进行旋转寻找最小的覆盖是困难的,但是由于点的位置都是相对的,因此可以将所有的点旋转,对于每一个角度,都存在一个覆盖,寻找最小的覆盖即可。
由于角度对于正方形的面积来说不是一个单调的函数,且角度是方案而不是最终的结果,因此必须选择三分的方式。
三分算法
坐标旋转公式
等三分会WA
#include
#include
using namespace std;
#define TMAX 33
int T, n;
double dot[TMAX][2];
double dot2[TMAX][2];
double cac(double ang) {
double x_max = 0, y_max = 0;
for (int i = 0; i < n; i++) {
dot2[i][0] = cos(ang) * dot[i][0] - sin(ang) * dot[i][1];
dot2[i][1] = sin(ang) * dot[i][0] + cos(ang) * dot[i][1];
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (fabs(dot2[i][0] - dot2[j][0]) > x_max) x_max = fabs(dot2[i][0] - dot2[j][0]);
if (fabs(dot2[i][1] - dot2[j][1]) > y_max) y_max = fabs(dot2[i][1] - dot2[j][1]);
}
}
return max(x_max, y_max) * max(x_max, y_max);
}
int main() {
scanf_s("%d", &T);
while (T--) {
scanf_s("%d", &n);
for (int i = 0; i < n; i++) scanf_s("%lf %lf", &dot[i][0], &dot[i][1]);
double L = 0, R = asin(1), M1, M2;
while (R - L > 1e-7) {
M1 = L + (R - L) / 3;
M2 = R - (R - L) / 3;
if (cac(M2) < cac(M1)) L = M1;
else R = M2;
//printf("%.2f %.2f %.7f\n", L, R, R-L);
}
printf("%.2f\n", cac(L));
}
}
二分然后再二分能够AC
#include
#include
using namespace std;
#define TMAX 33
int T, n;
double dot[TMAX][2];
double dot2[TMAX][2];
double cac(double ang) {
double x_max = 0, y_max = 0;
for (int i = 0; i < n; i++) {
dot2[i][0] = cos(ang) * dot[i][0] - sin(ang) * dot[i][1];
dot2[i][1] = sin(ang) * dot[i][0] + cos(ang) * dot[i][1];
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (fabs(dot2[i][0] - dot2[j][0]) > x_max) x_max = fabs(dot2[i][0] - dot2[j][0]);
if (fabs(dot2[i][1] - dot2[j][1]) > y_max) y_max = fabs(dot2[i][1] - dot2[j][1]);
}
}
//cout <
return max(x_max, y_max) * max(x_max, y_max);
}
int main() {
scanf_s("%d", &T);
while (T--) {
scanf_s("%d", &n);
for (int i = 0; i < n; i++) scanf_s("%lf %lf", &dot[i][0], &dot[i][1]);
double L = 0, R = asin(double(1)), M1, M2;
while (R - L > 1e-12) {
//M1 = L + (R - L) / 3;
//M2 = R - (R - L) / 3;
//printf("%.2f %.2f %.7f\n", L, R, R - L);
M1 = (L + R) / 2.0;
M2 = (R + M1) / 2.0;
if (cac(M2) <= cac(M1)) L = M1;
else R = M2;
}
printf("%.2f\n", cac(L));
}
}