旅行の售货员
//售货员の周游路线 P122
//从1出发,各点走一遍后回到起点
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define maxx 0x3f3f3f
int c, e; //【城市个数】【道路数量】
int w1[100], way1; //当前路径 当前总路程
int w2[100], best; //最佳路径 最小总路程
int G[100][100]; //图【出发点】【目标点】
void init()
{
way1 = 0;
best = maxx;
for (int k = 1; k <= c; k++)
w1[k] = k;
}
void get()
{
int p1, p2, l; //点1 点2 距离
printf("输入【城市数】【道路数】:\n");
scanf("%d%d", &c, &e);
memset(G, -1, sizeof(G));
printf("道路情况部署【P1 P2 Len】:\n");
for (int k = 1; k <= e; ++k)
{
scanf("%d%d%d", &p1, &p2, &l);
G[p1][p2] = G[p2][p1] = l;
}
init();
}
void swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
void ans()
{
printf("Min_Way=%d\n", best);
printf("Way_Back:");
for (int x = 1; x <= c; x++)
printf("%d ", w2[x]);
printf("1\n");
}
bool pd1() //【更小的总路程】
{
if (G[w1[c - 1]][w1[c]] != -1 && G[w1[c]][w1[1]] != -1 && (way1 + G[w1[c - 1]][w1[c]] + G[w1[c]][w1[1]] < best || best == maxx))
return true; //到叶路 叶归路 存在 且相加更优化
else //或者best还没更新过(防止相加后还比max大)
return false;
}
bool pd2(int k, int y)
{
if (G[w1[k - 1]][w1[y]] != -1 && (way1 + G[w1[k - 1]][w1[y]] < best || best == maxx))
return true; //到y点有路 当前局部比较优秀(剪枝)或 还没启动过
else
return false;
}
void Find_way(int k) // i迭代深度
{
if (k == c && pd1()) //若:探测到底,且为最小距离
{ //【最小距离】=当前距离+到叶距离+叶归距离
// k-1是因为递归指针刚来,走来位置k的距离还没算
best = way1 + G[w1[k - 1]][w1[k]] + G[w1[k]][w1[1]];
for (int x = 1; x <= c; x++) //保存路径
w2[x] = w1[x];
}
else // 还没探测到底,继续探测
{
for (int y = k; y <= c; y++) //【交换】当前点和此点之后的点(全排列)
{
if (pd2(k, y)) //剪枝判断
{ // k是【基准点】,y是【试图交换点】
swap(w1[k], w1[y]);
way1 += G[w1[k - 1]][w1[k]]; //探测,对【试图交换点】(已易位到基准点)
Find_way(k + 1); //递归
way1 -= G[w1[k - 1]][w1[k]]; //回溯
swap(w1[k], w1[y]);
}
}
}
}
int main()
{
get();
init();
Find_way(2);
ans();
return 0;
}
/*
4 6
1 2 30
1 3 6
1 4 4
2 4 10
2 3 5
3 4 20
*/
装载问题
//【5.2装载问题】
#include <iostream>
#include <stdio.h>
using namespace std;
int n, w[274], c1, c2;
int now, best;
bool way1[274], way2[274]; //当前路径,最佳路径
void init()
{
printf("输入【两船载货容量】:");
scanf("%d%d", &c1, &c2);
printf("输入【集装箱个数】:");
scanf("%d", &n);
for (int x = 1; x <= n; x++)
scanf("%d", &w[x]);
now = best = 0;
for (int x = 0; x <= n; x++)
way1[x] = false;
}
void work(int k)
{
if (k > n) //此时溢出,说明n个选择情况都已完成
{
if (now > best)
{
best = now;
for (int x = 1; x <= n; x++)
way2[x] = way1[x];
}
return;
}
if (now + w[k] <= c1) // C1船还放得下
{
//【装一下玩玩】
now += w[k];
way1[k] = true;
work(k + 1);
//【回溯拿出来】就当啥也没发生过
now -= w[k];
way1[k] = false;
}
// C1能放,但是我就是不放第k个,直接往下探索
work(k + 1);
}
void ans()
{
int sum = 0;
printf("货船1号最大载货量=%d\t集装箱编号:", best);
for (int x = 1; x <= n; x++)
{
if (way2[x])
printf("%d ", x);
else
sum += w[x];
}
printf("\n剩余集装箱总重:%d\t", sum);
if (sum > c2)
printf("大于货船2号载重总量%d\n任务失败!", c2);
else if (sum < c2)
printf("小于货船2号载重总量%d\n任务成功!", c2);
else
printf("恰好等于货船2号载重总量%d\n有惊无险!", c2);
}
int main()
{
init();
work(1);
ans();
return 0;
}
/*
50 50
3 10 40 40
50 50
3 20 40 40
*/
批处理作业调度
//【批处理作业调度】
#include <stdio.h>
#include <iostream>
#define maxx 0xfffffff
using namespace std;
int n, m[274][2]; //【作业总数】【批改所需用时】0是T1机,1是T2机
int f, bestf; //【当前·总时间】【最优·总时间】
int f1, f2; //【机器1·完成工作の时间戳】【机器2·完成工作の时间戳】
//由于T2机器会受到T1机器工作进度の影响,需要存储之前的工作情况
int way1[274], way2[274]; //【当前路径】【最佳路径】
void ans();
void init() //《数据输入》
{
printf("输入【作业总数】:");
scanf("%d", &n);
for (int x = 1; x <= n; x++)
scanf("%d%d", &m[x][0], &m[x][1]);
// 0是 T1用时 1是T2用时
f = f1 = f2 = 0;
bestf = 274540;
for (int x = 1; x <= n; x++)
way1[x] = x; //注意!这里要初始化!!缺失这里会导致异常
}
void swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
void work(int k) //《回溯·剪枝》 部署·第k个位置
{
if (k > n) //完成所有部署
{
if (f < bestf) //更优秀的结果
{
// printf("巧喑最可爱!");
bestf = f;
for (int x = 1; x <= n; x++)
way2[x] = way1[x];
}
return;
}
else
{
for (int x = k; x <= n; x++) //【交换·全排列】·与·后续所有数字尝试交换
{
int t = f2; //【F2先锋时间】
f1 += m[way1[x]][0]; //【机器T1·完成作业批改】后的时间戳
f2 = (f2 > f1 ? f2 : f1) + m[way1[x]][1]; //【机器T2·完成作业批改】后の时间戳
f += f2; //【当前作业T2完成实践】
//由【之前工作完成の时间点】+【当前作业批改所需用时】
//受到【机器T1】【前项任务进度】的影响,取最大值
//要么,机器T1还没改完,还不能开始工作
//要么,机器T2还没改完之前的作业,暂时不能响应当前的工作请求
if (f < bestf) //【剪枝】局部如果不够优秀,直接舍弃,不探索子树情况
{
swap(way1[k], way1[x]);
work(k + 1);
swap(way1[k], way1[x]);
}
f -= f2; //【当前作业T2完成时间】
f2 = t; //重要!还原f2为先锋时间
f1 -= m[way1[x]][0];
}
}
}
void ans() //《答案汇报》
{
printf("min_time=%d\n", bestf);
printf("Way:");
for (int x = 1; x <= n; x++)
printf("%d ", way2[x]);
printf("\n");
}
int main()
{
init();
work(1);
ans();
return 0;
}
/*
3
2 1
3 1
2 3
*/
n皇后问题Day72
//【n皇后问题】
#include <iostream>
#include <stdio.h>
#include <math.h>
using namespace std;
/*
【控制区部署】假定n=8
向下为x·行,向右为y列
【行锁定】·当前递归层数
【列锁定】·t 标记,共n个;对角总数为2*n-1
【主对角锁定】·x-y(-7~7之间,共15个);更迭为【x-y+n-1】0-->14
【副对角锁定】·x+y(2-16之间,共15个);更迭为【x+y-2】 0-->14
*/
int n, way[233]; //【几个皇后】【皇后分布】
int sum = 0; //【解の总数】
bool t[233], tx[466], ty[466]; //【占用标记:列 次对角 主对角】
// tx[x+y-2] ty[x-y+n-1]
void init()
{
printf("输入【皇后】总数:");
scanf("%d", &n);
for (int x = 0; x < 466; x++)
tx[x] = ty[x] = false;
for (int x = 0; x < 233; x++)
t[x] = false;
}
void ans()
{
printf("\n第%d个解:\n", sum);
for (int x = 1; x <= n; x++)
{
for (int y = 1; y <= n; y++)
{
if (y == way[x])
printf("★\t");
else
printf("☆\t");
}
printf("\n\n\n");
}
printf("\n");
}
bool pd(int x, int y)
{
if (!t[y] && !tx[x + y - 2] && !ty[x - y + n - 1])
return true;
return false;
}
void take(int x, int y)
{
t[y] = true;
tx[x + y - 2] = true;
ty[x - y + n - 1] = true;
way[x] = y;
}
void out(int x, int y)
{
t[y] = false;
tx[x + y - 2] = false;
ty[x - y + n - 1] = false;
}
void work(int x) //【坐标·行】
{
if (x > n)
{
sum++;
ans();
}
for (int y = 1; y <= n; y++) //【坐标·列】
{
if (pd(x, y)) //如果可以放置
{
take(x, y); //控制
work(x + 1); //向下递归
out(x, y); //释放
}
}
}
int main()
{
init();
work(1);
return 0;
}
01背包·回溯建树
//【01背包】·回溯
#include <iostream>
#include <stdio.h>
using namespace std;
int n, c, w[233], v[233]; //【商品数】【背包容量】【商品重量】【商品价值】
int vmax = -1, vnow = 0; //【最高价值】【当前价值】
bool snow[233], sbest[233]; //【当前选择】【最优选择】
int cnow; //【当前背包剩余容量】
void init()
{
printf("输入【背包容量】【商品总数】:");
scanf("%d%d", &c, &n);
printf("输入【商品重量】【商品价值】:\n");
for (int x = 1; x <= n; x++)
scanf("%d%d", &w[x], &v[x]);
for (int x = 1; x <= n; x++)
snow[x] = false;
cnow = c;
}
void work(int k)
{
if (k > n)
{
if (vnow > vmax)
{
vmax = vnow;
for (int x = 1; x <= n; x++)
sbest[x] = snow[x];
}
return;
}
//【选择】
if (cnow >= w[k]) //如果放得下
{
cnow -= w[k];
vnow += v[k];
snow[k] = true; //放入:占用重量,获得价值,标记放入
work(k + 1);
cnow += w[k]; //拿出:释放重量,减去价值,标记没放入
vnow -= v[k];
snow[k] = false;
}
//【不选择】
work(k + 1);
}
void ans()
{
printf("max=%d item:", vmax);
for (int x = 1; x <= n; x++)
if (sbest[x])
printf("%d ", x);
}
int main()
{
init();
work(1); //第几个物品
ans();
return 0;
}
/*
n物品,每个物品重w,价值v;背包总载重为c;要求价值最大,怎么选?
载重13,5个商品,先重量后价格;ans=28(123)
13 5
4 9
5 10
4 9
3 2
10 24
*/
着色问题(全部方案求解/最少颜料的着色方案)
//【图の着色问题】·最少颜料使用情况
#include <iostream>
#include <stdio.h>
using namespace std;
#define maxx 0xfffffff
bool t[233][233]; //【地图】
int n, m; //【点边 数】
int c1[233]; //【当前着色策略】什么点,用什么色
int now = 0; //【当前着色の最大编号】
int sum = 0; //【着色方案个数】
bool endd = false; //【最优搜索结束标志】
//最差の情况:n个点颜色都不一样
void ans()
{
printf("\nNum=%d Strategy=", ++sum);
for (int x = 1; x <= n; x++)
printf("%d ", c1[x]);
printf("Min_Color=%d\n", now);
}
void test()
{
for (int x = 1; x <= n; x++)
{
for (int y = 1; y <= n; y++)
{
if (t[x][y])
printf("1");
else
printf("0");
printf("\t");
}
printf("\n\n\n");
}
}
void init()
{
printf("输入【点·边 总数】:");
scanf("%d%d", &n, &m);
printf("输入%d条边,各边连接的【俩端点】\n", m);
int a, b;
for (int x = 0; x < 233; x++)
for (int y = 0; y < 233; y++)
t[x][y] = false;
for (int x = 1; x <= m; x++)
{
scanf("%d%d", &a, &b);
t[a][b] = t[b][a] = true;
}
// test();
}
bool pd(int k)
{
for (int x = 1; x <= n; x++)
if (t[x][k] && c1[x] == c1[k]) //【相邻点】能走得通,且颜色一样
return false;
return true;
}
void work(int k)
{
if (endd == true)
return;
if (k > n) //第一次找到的就是最佳方案
{
ans();
endd = true;
now = 0;
return;
}
//【当前点第k号】
for (int x = 1; x <= n; x++) //尝试部署一种颜色
{
c1[k] = x; //部署第x种颜色
if (pd(k)) //有效部署
{
if (now < x) //许多编号,选最大的一个
now = x; //【当前使用的着色编号】
work(k + 1);
}
c1[k] = 0; //部署颜色x撤回
}
}
int main()
{
init();
work(1);
return 0;
}
/*
5 8
1 3
1 2
1 4
2 4
2 5
2 3
3 4
4 5
*/
/*
【全部求解情况】
//【图の着色问题】·追踪最优方案(其实就是第一个)
#include <iostream>
#include <stdio.h>
using namespace std;
#define maxx 0xfffffff
bool t[233][233]; //【地图】
int n, m; //【点边 数】
int c1[233]; //【当前着色策略】什么点,用什么色
int sum = 0; //【着色方案个数】
//最差の情况:n个点颜色都不一样
void ans()
{
printf("\nNum=%d Strategy=", ++sum);
for (int x = 1; x <= n; x++)
printf("%d ", c1[x]);
}
void test()
{
for (int x = 1; x <= n; x++)
{
for (int y = 1; y <= n; y++)
{
if (t[x][y])
printf("1");
else
printf("0");
printf("\t");
}
printf("\n\n\n");
}
}
void init()
{
printf("输入【点·边 总数】:");
scanf("%d%d", &n, &m);
printf("输入%d条边,各边连接的【俩端点】\n", m);
int a, b;
for (int x = 0; x < 233; x++)
for (int y = 0; y < 233; y++)
t[x][y] = false;
for (int x = 1; x <= m; x++)
{
scanf("%d%d", &a, &b);
t[a][b] = t[b][a] = true;
}
// test();
}
bool pd(int k)
{
for (int x = 1; x <= n; x++)
if (t[x][k] && c1[x] == c1[k]) //【相邻点】能走得通,且颜色一样
return false;
return true;
}
void work(int k)
{
if (k > n) //第一次找到的就是最佳方案
{
ans();
return;
}
//【当前点第k号】
for (int x = 1; x <= n; x++) //尝试部署一种颜色
{
c1[k] = x; //部署第x种颜色
if (pd(k)) //有效部署
work(k + 1);
c1[k] = 0; //部署颜色x撤回
}
}
int main()
{
init();
work(1);
return 0;
}
*/
圆排列问题
//【圆排列问题】n个圆,底边都相切于同一矩形,要求矩形的狂宽度最小
#include <iostream>
#include <stdio.h>
#include <math.h>
using namespace std;
#define maxx 0xfffffff
int n; //【圆の个数】
float r[2745], x[2745]; //【圆の排列·当前】 【横坐标情况·当前】
float minn = maxx; //【最小の长度·全局】
float best[2745]; //【最好の排列方式】
void init()
{
printf("输入【圆の数量】:");
scanf("%d", &n);
for (int t = 1; t <= n; t++)
x[t] = 0;
for (int x = 1; x <= n; x++)
scanf("%f", &r[x]);
}
void swap(float *a, float *b)
{
float c = *a;
*a = *b;
*b = c;
}
float X(int k) //返回第t个圆的横坐标x
{
float t = 0; //【t最长の长度】【v临时の长度】 之前共k-1个圆
for (int i = 1; i < k; i++) //【极端情况】当前k有可能与之前任意一个圆i相切
{
float v = x[i] + 2 * sqrt(r[i] * r[k]);
if (v > t)
t = v;
}
return t;
}
void test(float a)
{
printf("\n【最佳情况】%f\n", a);
printf("\n 横坐标·半径 情况汇报:\n");
for (int i = 1; i <= n; i++)
printf("%f ", x[i]);
printf("\n");
for (int i = 1; i <= n; i++)
printf("%f ", r[i]);
printf("\n");
}
void all() //计算当前全区长度
{
float high = 0, low = 0;
for (int i = 1; i <= n; i++)
{
if (x[i] - r[i] < low)
low = x[i] - r[i];
if (x[i] + r[i] > high)
high = x[i] + r[i];
}
if (high - low < minn)
{
minn = high - low;
for (int i = 1; i <= n; i++)
{
best[i] = r[i];
}
// test(minn);
}
}
void work(int k)
{
if (k > n)
all();
else
{
for (int i = k; i <= n; i++)
{
swap(r[i], r[k]);
float cx = X(k); //【全排列】部署完成第k个位置
if (cx + r[1] + r[k] < minn) //【剪枝】局部相对最优
{ //有效部署!
x[k] = cx;
work(k + 1);
}
swap(r[i], r[k]);
}
}
}
void ans()
{
printf("Min=%.4f\nBest_Array:", minn);
for (int i = 1; i <= n; i++)
{
printf("%.2f ", best[i]);
}
}
int main()
{
init();
work(1);
ans();
return 0;
}
//注意极端情况
/*
4
50 0.1 100 20
*/
这种情况下13相切,2和1相切;对3号x坐标判断会产生异常。
这个决定了函数x中,需要覆盖所有相切可能性(t可能与t之前任意一个圆形相切)
决定了函数all中,总长度定界需要遍历每一个点看左右界限
而不是只是看最左侧或者最右侧情况
【总体思想】:对圆进行全排列
函数X:返回【第t个圆·当前部署】的横坐标
t是最长x的情况,i遍历【k之前の所有圆】·假设与他相切
【第t个圆の横坐标】=【第i个圆の横坐标】+相切时的纵向情况
函数ALL:在全部部署完成后,对总长度进行一个判断
遍历所有点,根据当前点的x和r给出左右边界,是否能【延伸左右边界】
极端情况举例 0.1 200 0.1 0.2,这时候左右边界是200决定,而非左右的0.1
左右0.1在200下面的空隙处,起不到作用
【局部剪枝】本身目的是:【当前排列总长度<min就继续】
但具体实践中使用的却是【最左侧】【最右侧】,似乎是个无奈之举
因为如果真去判断当前排列总长度(参考all),代价太大
这里使用的是一个【折中的方法】
(这里似乎没有考虑到极端情况的发生,只是【最左侧】和【最右侧(当前)】进行一个长度计算,比当前min要小,就继续)
其实仔细思考可以发现,假如0.1 200 0.1 0.1这种数据进去,如果有内嵌情况
d一定是变小的,是可以被这个剪枝覆盖进去的,不会丢失情况,
只是可能会多产生几次计算(剪枝不彻底)
这确实是无奈之举,因为相较于精确判断要支付的代价
这样多计算几个叶子结点的全排列情况也许更合算