「THUWC 2017」大葱的神力 - 题解

洛谷P1724题解(请不要抄袭!) 洛谷P1427题解,若要复制or下载,请与我联系,如要改编,也请联系,若发现未经我同意的复制、下载、改编,直接举报。 立即下载
忠告:如果你想抄题解的,可以离开,这不是一时半会儿能解决的问题

前置知识:

  1. 学习笔记:费用流icon-default.png?t=N7T8https://blog.youkuaiyun.com/weixin_44043668/article/details/108738212
  2. C++动态规划详解icon-default.png?t=N7T8https://blog.youkuaiyun.com/weixin_51951103/article/details/120241450
  3. C++ 背包问题icon-default.png?t=N7T8https://blog.youkuaiyun.com/weixin_43899008/article/details/124738946
  4. 模拟退火算法(c++实现)(VIP文章)icon-default.png?t=N7T8https://blog.youkuaiyun.com/qq547276542/article/details/77800776
  5. TSP--模拟退火算法(c++实现+详细解释)icon-default.png?t=N7T8https://blog.youkuaiyun.com/qq_40738840/article/details/84324494

测试点1:

观察数据点 n = m = 5。

1 号,2 号,3 号,5 号物品都可以达到自己的最优选择。而枚举 4 号的物品的摆放会发现只有 4 号物品放入第 1 个背包时才能达到最优解。
最后玩出来的结果是:

3
3
2
1
1

测试点2、8、9、10:

测试点2:

观察数据 n=25,m=8。
理论上可以用搜索 + 剪枝,但是因为我实在太懒了,所以直接写了一个模拟退火。
跑了大概 3 分钟跑出来了最优解 108。

测试点8、9、10:

观察数据,无规律。

直接写随机化算法吧,模拟退火就是个不错的选择,代码细节我们等会儿来说。

据说这个退火算法坚持跑1\sim 2年可以跑出结果。。。

代码:
#include <cmath>
#include <ctime>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int MAXN = 1000; // 定义最大商品数
const int MAXM = 300;  // 定义最大抽屉数
const int INF = (1 << 30); // 定义一个非常大的数,作为无穷大的标记

int V1[MAXN + 5], V2[MAXN + 5], val[MAXN + 5][MAXM + 5]; // 商品价值,抽屉大小和商品在抽屉中的价值
int ans[MAXN + 5], tmp[MAXN + 5], nw[MAXN + 5], res = 0, N, M; // 当前结果,临时数组,当前方案,最优解的价值,商品数和抽屉数

// 获取当前方案的总价值
int get_ans() {
	int ret = 0, j = 1, re = V2[1];
	for (int i = 1; i <= N; i++) {
		if (j > M)
			tmp[nw[i]] = 0;
		else if (re >= V1[nw[i]]) {
			tmp[nw[i]] = j, re -= V1[nw[i]];
			ret += val[nw[i]][j];
		} else
			j++, i--, re = V2[j];
	}
	return ret;
}

int real_ans[MAXN + 5], real_res = 0; // 最优方案和最优价值

int main() {
	srand(time(NULL)); // 使用当前时间来初始化随机数生成器

	freopen("drawer8.in", "r", stdin); // 从文件读取输入

	scanf("%d%d", &N, &M); // 读取商品数和抽屉数
	for (int i = 1; i <= N; i++)
		scanf("%d", &V1[i]); // 读取每个商品的价值
	for (int i = 1; i <= M; i++)
		scanf("%d", &V2[i]); // 读取每个抽屉的大小
	for (int i = 1; i <= N; i++)
		for (int j = 1; j <= M; j++)
			scanf("%d", &val[i][j]); // 读取商品在抽屉里的价值

	// 使用模拟退火算法来求解
	while (true) {
		for (int i = 1; i <= N; i++)
			nw[i] = i;
		random_shuffle(nw + 1, nw + N + 1); // 随机生成一种方案
		res = get_ans(); // 计算当前方案的价值

		for (double T = 1E12; T >= 1E-5; T *= 0.986) { // 模拟退火过程
			int x = rand() % N + 1, y = rand() % N + 1;
			swap(nw[x], nw[y]); // 随机交换两个商品
			if (get_ans() > res || 1.0 / (1 + exp((res - get_ans()) / T)) >= 1.0 / rand()) {
				for (int j = 1; j <= N; j++)
					ans[j] = tmp[j]; // 更新当前最优解
				res = get_ans(); // 获取新的价值
			} else
				swap(nw[x], nw[y]); // 恢复之前的方案
		}

		// 更新历史最优解
		if (res > real_res) {
			real_res = res;
			for (int i = 1; i <= N; i++)
				real_ans[i] = ans[i]; // 记录新的最优方案
			FILE *f = fopen("drawer8.out", "w"); // 打开文件用于写入结果
			for (int i = 1; i <= N; i++)
				fprintf(f, "%d\n", real_ans[i]); // 将最优方案写入文件
			printf("%d\n", real_res); // 在控制台输出最优解的价值
		}
	}
}

测试点3:

观察数据 m=1
很经典的 01 背包,直接写 dp 就可以过了。

代码:
#include <cstdio>
#include <algorithm>
using namespace std;

// 定义最大物品数量和最大价值
const int MAXN = 2000;
const int MAXV = 10000;

// dp[i][j] 表示考虑前i件物品,总体积不超过j的情况下,可以获得的最大价值
int dp[MAXN + 5][MAXV + 5];
// V[i] 表示第i件物品的体积
int V[MAXN + 5];
// pre[i][j] 表示获取dp[i][j]的最大价值时,是否选择了第i件物品
bool pre[MAXN + 5][MAXV + 5];
// tag[i] 表示最终解中是否选择了第i件物品
bool tag[MAXN + 5];

// 递归函数,用于找出选择的物品
void get_ans(int i, int j) {
    if (i == 0) return; // 如果已经考虑完所有物品,则返回
    if (pre[i][j]) { // 如果选择了第i件物品
        tag[i] = true; // 标记为选择
        get_ans(i - 1, j - V[i]); // 继续考虑前一个物品,容量减少V[i]
    } else {
        get_ans(i - 1, j); // 如果没有选择第i件物品,直接考虑前一个物品
    }
}

int main() {
    int N, M, K;
    scanf("%d%d", &N, &M); // 读入物品数量N和背包容量M(这里的M未使用)
    for (int i = 1; i <= N; i++)
        scanf("%d", &V[i]); // 读入每个物品的体积
    scanf("%d", &K); // 读入背包的容量限制
    for (int i = 1; i <= N; i++) {
        int k;
        scanf("%d", &k); // 读入每个物品的价值
        for (int j = K; j >= V[i]; j--) {
            // 更新dp数组
            if (dp[i - 1][j - V[i]] + k > dp[i - 1][j]) {
                dp[i][j] = dp[i - 1][j - V[i]] + k; // 更新最大价值
                pre[i][j] = true; // 记录选择了这个物品
            } else {
                dp[i][j] = dp[i - 1][j]; // 不选择这个物品
                pre[i][j] = false;
            }
        }
    }
    get_ans(N, K); // 回溯找出选择的物品
    for (int i = 1; i <= N; i++)
        puts(tag[i] ? "1" : "0"); // 输出每个物品是否被选择
    // printf("%d\n", dp[N][K]); // 可以输出最大价值
}

测试点4、5、6:

测试点4:

观察数据,所有物品的体积都是 233。

这个时候就可以确定每个背包能装的物品数量是多少,背包容量和物体体积转换为了物品数量限制。

这个时候就是一个最大权匹配,写个费用流就可以了。

测试点5:

观察数据,所有物品的体积都是 2233。

跟 测试点4 一样的解法,只是时间跑得久一些,大概 5 秒。

测试点6:

观察数据,所有物品的体积都是 19260817 在 19660600 \sim 19660720 这个范围以内。

体积不一样,好像并不能再使用费用流了?

但实际上物品之间的体积都是微小扰动造成的,也就是说物品之间的体积差相对于物品体积本身而言非常微小,以至于可以忽略不计。

所以我们直接把物品体积当作所有物品体积的最大值来算就可以了。

代码:
#include <queue>
#include <cstdio>
#include <algorithm>
using namespace std;

// 节点和边的最大数目
const int MAXN = 1000;
const int MAXM = 700;
const int MAXV = MAXN + MAXM;
const int INF = (1 << 30);

struct edge {
	int to, flow, cap, dis;
	edge *nxt, *rev;
} edges[4 * MAXN * MAXM + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;

// 定义一个流图数据结构
struct flow_graph {
	int S, T, cost, dist[MAXV + 5]; // S源点,T汇点,cost总费用,dist到各点最短费用
	bool vis[MAXV + 5], inq[MAXV + 5]; // vis检查是否访问过,inq检查是否在队列中
	deque<int>que; // 双端队列,用于执行SPFA算法

	// 初始化
	void init() {
		ecnt = &edges[0];
	}

	// 添加边
	void addedge(int u, int v, int c, int w) {
		edge *p = (++ecnt), *q = (++ecnt);
		p->to = v, p->cap = c, p->dis = w, p->flow = 0;
		p->nxt = adj[u], adj[u] = p;
		q->to = u, q->cap = 0, q->dis = -w, q->flow = 0;
		q->nxt = adj[v], adj[v] = q;
		p->rev = q, q->rev = p;
	}

	// 执行SPFA算法,寻找最短费用的增广路径
	bool relabel() {
		for (int i = S; i <= T; i++)
			dist[i] = INF, cur[i] = adj[i];
		que.push_back(S);
		dist[S] = 0;
		inq[S] = true;
		while ( !que.empty() ) {
			int f = que.front();
			que.pop_front();
			inq[f] = false;
			for (edge *p = adj[f]; p != NULL; p = p->nxt) {
				if ( p->cap > p->flow ) {
					if ( dist[p->to] > dist[f] + p->dis ) {
						dist[p->to] = dist[f] + p->dis;
						if ( !inq[p->to] ) {
							inq[p->to] = true;
							if ( !que.empty() && dist[p->to] < dist[que.front()] )
								que.push_front(p->to);
							else que.push_back(p->to);
						}
					}
				}
			}
		}
		return !(dist[T] == INF);
	}

	// 增广路径
	// x当前节点,tot要增广的流量
	int aug(int x, int tot) {
		if ( x == T ) {
			cost += tot * dist[T]; // 更新总费用
			return tot;
		}
		int sum = 0;
		vis[x] = true;
		for (edge *&p = cur[x]; p != NULL; p = p->nxt) {
			if ( p->cap > p->flow && !vis[p->to] && dist[x] + p->dis == dist[p->to] ) {
				int del = aug(p->to, min(tot - sum, p->cap - p->flow));
				sum += del, p->flow += del, p->rev->flow -= del;
				if ( sum == tot ) break;
			}
		}
		vis[x] = false;
		return sum;
	}

	// 最小费用流算法
	int min_cost_max_flow() {
		int flow = 0;
		while ( relabel() )
			flow += aug(S, INF);
		return flow;
	}
} G;

int V1[MAXN + 5], V2[MAXN + 5];

// 主函数
int main() {
	int N, M, mx = 0;
	scanf("%d%d", &N, &M); // N节点数目,M边数目
	G.init();
	G.S = 0, G.T = N + M + 1; // 初始化图,设置源点和汇点
	for (int i = 1; i <= N; i++) {
		scanf("%d", &V1[i]); // 输入V1数组
		G.addedge(G.S, i, 1, 0); // 源点连接V1中的每个节点,边权为1,费用为0
		mx = max(mx, V1[i]);
	}
	for (int i = 1; i <= M; i++) {
		scanf("%d", &V2[i]); // 输入V2数组
		G.addedge(N + i, G.T, V2[i] / mx, 0); // V2中的每个节点连接汇点,边权为V2[i]/mx,费用为0
	}
	for (int i = 1; i <= N; i++)
		for (int j = 1; j <= M; j++) {
			int k;
			scanf("%d", &k); // 输入费用矩阵
			G.addedge(i, N + j, 1, -k); // 连接V1和V2,边权为1,费用为-k
		}
	G.min_cost_max_flow(); // 执行最小费用最大流算法
	for (int i = 1; i <= N; i++) {
		int ans = N;
		for (edge *p = adj[i]; p != NULL; p = p->nxt)
			if ( p->to != G.S && p->flow == 1 ) ans = p->to; // 配对结果
		printf("%d\n", ans - N); // 输出配对结果
	}
//  printf("%d\n", -G.cost); // 如果需要输出总费用,取消注释这一行
}

测试点7:

观察数据,除了物品 1 的体积为 46972 以外,其他物品的体积都为 11743。

我们直接枚举物品 1 的摆放位置,再跑费用流,取最优值。

时间复杂度有点高,跑了 1 分钟左右才跑出来最优解。

代码:(由于👆面有注释,所以这段不注释了~)
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 500;
const int MAXM = 100;
const int MAXV = MAXN + MAXM;
const int INF = (1<<30);
struct edge{
    int to, flow, cap, dis;
    edge *nxt, *rev;
}edges[4*MAXN*MAXM + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
struct flow_graph{
    int S, T, cost, dist[MAXV + 5];
    bool vis[MAXV + 5], inq[MAXV + 5];
    deque<int>que;
    void init() {
        ecnt = &edges[0];
        for(int i=S;i<=T;i++)
            adj[i] = NULL;
        cost = 0;
    }
    void addedge(int u, int v, int c, int w) {
        edge *p = (++ecnt), *q = (++ecnt);
        p->to = v, p->cap = c, p->dis = w, p->flow = 0;
        p->nxt = adj[u], adj[u] = p;
        q->to = u, q->cap = 0, q->dis = -w, q->flow = 0;
        q->nxt = adj[v], adj[v] = q;
        p->rev = q, q->rev = p;
    }
    bool relabel() {
        for(int i=S;i<=T;i++)
            dist[i] = INF, cur[i] = adj[i];
        que.push_back(S); dist[S] = 0; inq[S] = true;
        while( !que.empty() ) {
            int f = que.front(); que.pop_front(); inq[f] = false;
            for(edge *p=adj[f];p!=NULL;p=p->nxt) {
                if( p->cap > p->flow ) {
                    if( dist[p->to] > dist[f] + p->dis ) {
                        dist[p->to] = dist[f] + p->dis;
                        if( !inq[p->to] ) {
                            inq[p->to] = true;
                            if( !que.empty() && dist[p->to] < dist[que.front()] )
                                que.push_front(p->to);
                            else que.push_back(p->to);
                        }
                    }
                }
            }
        }
        return !(dist[T] == INF);
    }
    int aug(int x, int tot) {
        if( x == T ) {
            cost += tot*dist[T];
            return tot;
        }
        int sum = 0; vis[x] = true;
        for(edge *&p=cur[x];p!=NULL;p=p->nxt) {
            if( p->cap > p->flow && !vis[p->to] && dist[x] + p->dis == dist[p->to] ) {
                int del = aug(p->to, min(tot - sum, p->cap - p->flow));
                sum += del, p->flow += del, p->rev->flow -= del;
                if( sum == tot ) break;
            }
        }
        vis[x] = false;
        return sum;
    }
    int min_cost_max_flow() {
        int flow = 0;
        while( relabel() )
            flow += aug(S, INF);
        return flow;
    }
}G;
int V1[MAXN + 5], V2[MAXN + 5], val[MAXN + 5][MAXM + 5];
int ans[MAXN + 5], res = 0;
int main() {
    int N, M; scanf("%d%d", &N, &M);
    G.S = 0, G.T = N + M + 1; G.init();
    for(int i=1;i<=N;i++)
        scanf("%d", &V1[i]);
    for(int i=1;i<=M;i++)
        scanf("%d", &V2[i]);
    for(int i=1;i<=N;i++)
        for(int j=1;j<=M;j++)
            scanf("%d", &val[i][j]);
    for(int i=2;i<=N;i++)
        for(int j=1;j<=M;j++)
            G.addedge(i, N + j, 1, -val[i][j]);
    for(int i=2;i<=N;i++)
        G.addedge(G.S, i, 1, 0);
    for(int i=1;i<=M;i++)
        G.addedge(N + i, G.T, V2[i]/V1[2], 0);
    G.min_cost_max_flow(); res = -G.cost;
    for(int i=2;i<=N;i++) {
        int flag = N;
        for(edge *p=adj[i];p!=NULL;p=p->nxt)
            if( p->to != G.S && p->flow == 1 ) flag = p->to;
        ans[i] = flag - N;
    }
    for(int i=1;i<=M;i++) {
        if( V2[i] < V1[1] ) continue;
        G.init();
        for(int j=2;j<=N;j++)
            for(int k=1;k<=M;k++)
                G.addedge(j, N + k, 1, -val[j][k]);
        for(int j=2;j<=N;j++)
            G.addedge(G.S, j, 1, 0);
        for(int j=1;j<=M;j++)
            if( j == i ) G.addedge(N + j, G.T, (V2[j] - V1[1])/V1[2], 0);
            else G.addedge(N + j, G.T, V2[j]/V1[2], 0);
        G.min_cost_max_flow();
        if( res < -G.cost + val[1][i] ) {
            res = -G.cost + val[1][i]; ans[1] = i;
            for(int i=2;i<=N;i++) {
                int flag = N;
                for(edge *p=adj[i];p!=NULL;p=p->nxt)
                    if( p->to != G.S && p->flow == 1 ) flag = p->to;
                ans[i] = flag - N;
            }
        }
    }
    for(int i=1;i<=N;i++)
        printf("%d\n", ans[i]);
//  printf("%d\n", res);
}

至此,题解部分已经结束,下面是闲谈(可能对你有很大帮助)。

如果你认为这篇题解写的不错的话……动动手指,一键三连吧!👇👇👇


以下所有内容仅供参考,烧显卡本人不负责!

前置神力:

  • 一台 Windows10 或 Windows Server 2019 服务器
  • CPU:至少 Intel E5-2680V4(16 核心 32 线程)
  • 内存:至少 64 GiB
  • 显卡:至少 Nvidia Tesla P40 和一块 Nvidia Tesla M40(显存一共至少 24 GiB)
  • 以上条件为必要,可适当低配(如果没有,也可以继续看下去,有解决办法)。。
  • 网络:100Mbps 及以上下载带宽,可能需要科学上网(自行百度,不在文章范围。。)
插话:解决物理神力的问题

你得去一些云服务商租借 GPU云服务器,比如 35003500 一个月的阿里云或者一个月 待更新待更新 元,对学生党友好的雨云。

但……如果你没有零花钱(或者不知道云服务器怎么用),可以不用往下面看了……


正题:

众所周知,这题可以用模拟退火来做

这时间复杂度……O(n!)O(n!) 我都快疯了!!!,开了几个晚上啊!

我们可以考虑将程序挂载到 GPU 上运行(关键!!!)

插话:GPU 是什么东西?

GPU 就是 图形处理器 的缩写,它可以提供更高的并行运算能力,在 3D设计 游戏开发 图形处理中均有广泛的应用。

当然,GPU 也有他的缺点:价格过高、发热过猛、容易烧坏……

这是一张 GPU 价格一览表:

型号显存价格
Nvidia RTX 409024 GiB5050¥
Nvidia Tesla P4024 GiB1080¥
Nvidia Tesla K8024 GiB899¥
Nvidia Tesla M4024 GiB555¥

如何挂载到 GPU 上?

首先,我们来看一个 C++ 程序:

#include <bits/stdc++.h>
#include <cuda_runtime.h>
#include <cuda.h>
using namespace std;
int main() {
    ios :: sync_with_stdio(false);
    
    return 0;
}

其中的 #include <cuda_runtime.h> 就是调用 NvidiaNvidia 显卡的库文件,然后就可以挂载显卡了!!

别高兴,我们先得安装驱动~

进入这里,下载 CUDA 和显卡驱动程序,一定要选择对应的版本!

==注意:一定要选择离线版本,否则就只能魔法安装了!==

安装完成之后,就可以安装 Microsoft Visual Studio 2022 了,建议安装 20222022 版本,安装时注意选择跟 C++ 有关的所有模块。

安装完成后,我们测试一个程序,看看能不能正常运行:

#include <bits/stdc++.h>
using namespace std;
int n;
int main() {
    ios :: sync_with_stdio(false);
    cin >> n;
    cout << n * n << endl;
    vector <int> a;
    int tmp;
    for (int i = 1; i <= n; i ++) {
        cin >> tmp;
        a . push_back(tmp);
    }
    cout << a . size() << endl;
    return 0;
}
测试 GPU 性能&改编程序

如果你的 GPU 不是前面所讲的,那么你可以将 Stable Diffusion 本地化部署:(具体在这里看教程)

如果你的电脑出一张 1024x1024 的图片在 10s 之内的话,就可以继续了~

改编程序:

#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cuda.h>
#include<cuda_runtime.h>
using namespace std;
const int MAXN = 1000;
const int MAXM = 300;
const int INF = (1<<30);
int V1[MAXN + 5], V2[MAXN + 5], val[MAXN + 5][MAXM + 5];
int ans[MAXN + 5], tmp[MAXN + 5], nw[MAXN + 5], res = 0, N, M;
int get_ans() {
	int ret = 0, j = 1, re = V2[1];
	for(int i=1; i<=N; i++) {
		if( j > M ) tmp[nw[i]] = 0;
		else if( re >= V1[nw[i]] ) {
			tmp[nw[i]] = j, re -= V1[nw[i]];
			ret += val[nw[i]][j];
		} else j++, i--, re = V2[j];
	}
	return ret;
}
int real_ans[MAXN + 5], real_res = 0;
int main() {
	srand(time(NULL));
	freopen("drawer8.in", "r", stdin);
	scanf("%d%d", &N, &M);
	for(int i=1; i<=N; i++)
		scanf("%d", &V1[i]);
	for(int i=1; i<=M; i++)
		scanf("%d", &V2[i]);
	for(int i=1; i<=N; i++)
		for(int j=1; j<=M; j++)
			scanf("%d", &val[i][j]);
	while( true ) {
		for(int i=1; i<=N; i++)
			nw[i] = i;
		random_shuffle(nw + 1, nw + N + 1);
		res = get_ans();
		for(register double T=1E12; T>=1E-5; T*=0.986) {
			int x = rand() * 17 % N + 1, y = rand() * 17 % N + 1;
			swap(nw[x], nw[y]);
			if( get_ans() > res || 1.0/(1 + exp((res - get_ans())/T)) >= 1.0/rand() ) {
				for(int j=1; j<=N; j++)
					ans[j] = tmp[j];
				res = get_ans();
			} else swap(nw[x], nw[y]);
		}
		if( res > real_res ) {
			real_res = res;
			for(int i=1; i<=N; i++)
				real_ans[i] = ans[i];
			FILE *f = fopen("drawer8.out", "w");
			for(int i=1; i<=N; i++)
				fprintf(f, "%d\n", real_ans[i]);
			printf("%d\n", real_res);
		}
	}
}

==可以注意到,我们在每一个函数前面加上了一个 _即将更新_,就代表这个函数依附于 GPU 运行。==

同时,我们尝试将变量也该用 GPU显存,具体取决于你的电脑驱动了:

#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cuda.h>
#include<cuda_runtime.h>
using namespace std;
const int MAXN = 1000;
const int MAXM = 300;
const int INF = (1<<30);
int V1[MAXN + 5], V2[MAXN + 5], val[MAXN + 5][MAXM + 5];
int ans[MAXN + 5], tmp[MAXN + 5], nw[MAXN + 5], res = 0, N, M;
int get_ans() {
	int ret = 0, j = 1, re = V2[1];
	for(int i=1; i<=N; i++) {
		if( j > M ) tmp[nw[i]] = 0;
		else if( re >= V1[nw[i]] ) {
			tmp[nw[i]] = j, re -= V1[nw[i]];
			ret += val[nw[i]][j];
		} else j++, i--, re = V2[j];
	}
	return ret;
}
int real_ans[MAXN + 5], real_res = 0;
int main() {
	srand(time(NULL));
	freopen("drawer8.in", "r", stdin);
	scanf("%d%d", &N, &M);
	for(int i=1; i<=N; i++)
		scanf("%d", &V1[i]);
	for(int i=1; i<=M; i++)
		scanf("%d", &V2[i]);
	for(int i=1; i<=N; i++)
		for(int j=1; j<=M; j++)
			scanf("%d", &val[i][j]);
	while( true ) {
		for(int i=1; i<=N; i++)
			nw[i] = i;
		random_shuffle(nw + 1, nw + N + 1);
		res = get_ans();
		for(register double T=1E12; T>=1E-5; T*=0.986) {
			int x = rand() * 17 % N + 1, y = rand() * 17 % N + 1;
			swap(nw[x], nw[y]);
			if( get_ans() > res || 1.0/(1 + exp((res - get_ans())/T)) >= 1.0/rand() ) {
				for(int j=1; j<=N; j++)
					ans[j] = tmp[j];
				res = get_ans();
			} else swap(nw[x], nw[y]);
		}
		if( res > real_res ) {
			real_res = res;
			for(int i=1; i<=N; i++)
				real_ans[i] = ans[i];
			FILE *f = fopen("drawer8.out", "w");
			for(int i=1; i<=N; i++)
				fprintf(f, "%d\n", real_ans[i]);
			printf("%d\n", real_res);
		}
	}
}

你可以尝试运行;如果没报错,恭喜,可以跳过解决方案!


编译器提示没有找到 cuda_runtime.h 的解决方案

[错误]cuda_runtime.h:Nosuchfileordirectory[错误]cuda_runtime.h:Nosuchfileordirectory

别着急,先看看你的驱动是否安装成功?驱动是否启动?显卡是否插好?==驱动版本是否匹配???==

(在前面提到过)

如果没问题?看看你的 Visual Studio 2022 编译器是否设置为 -std=c++20?==是否创建的是C++ CUDA 项目???==

若都没问题,可以重装 Microsoft Visual Studio 2022 了……

(看前面的安装过程)

如果还是不行,可以在评论留言或==仔细再看一遍教程==

如果还没有找到问题,可以==去开头项目文件中下载最新的项目文件和 exe测试文件,看看有没有问题。==


运行程序:

大概运行一周左右吧……

结果就出来了……


如何判断结果是否正确?

到 Loj 上去下载答案文件,==不要天真的以为那就是AC答案==

然后对比运行程序后输出在控制台而不是文件中的内容,看最后一个是否是你的程序比 Loj 的更大或相等,就可以了!

CCF WC2017 冬令营 课件集合 (圆方树等) CCF WC2017 课件集合 (圆方树等) CCF WC2017 课件集合 (圆方树等) CCF WC2017 课件集合 (圆方树等) 立即下载

相关推荐

逍遥模拟器改真机参数全指南:从ARM伪装到电池传感器模拟

本文提供逍遥模拟器深度伪装实战指南,重点讲解如何通过修改ARM架构、系统属性和模拟传感器数据,使其在硬件信息、电池状态等维度逼近真实设备,有效规避应用检测。文章详细介绍了使用Magisk模块、LSPosed框架进行设备指纹伪装和动态数据模拟的策略,帮助用户实现从静态信息到动态行为的全方位真机模拟。

weixin_29279101的博客 452

洛谷 P6849 [THUWC2017] 大葱的神力 题解

具体来说,我们生成物品的一个排列,然后贪心地将物品依次“尝试”放入 1~M 个背包,计算当前取得的最优值。这个代码的主体是费用流,所以可以复制上面的代码再稍微改改。,要不是因为这是题答,我还真不敢开这么大的数组。用模拟退火来生成排列就是一个很经典的应用了。T2,T8~10交给你们自己跑了。快乐一换三的代码,不要白不要。数据不大,自己写搜索吧!我电脑霍霍不起(逃)。

zsr201276的博客 745

第4章 本地一键部署(新手首选)

本文介绍了OpenClaw的本地部署方法,特别适合新手用户。部署过程仅需执行一条命令,通过自动脚本完成安装和配置向导。安装完成后,用户可通过终端TUI或Web控制面板验证部署是否成功。文章还提供了常见问题的排查方法,包括端口占用、权限不足、APIKey无效和安装卡顿等情况。整个部署流程简单直观,无需手动修改配置文件,适合快速搭建使用。

eastyuxiao的专栏 193

@loj - 2288@「THUWC 2017」大葱的神力

目录 @description@ @solution@ @data - 1@ @data - 2@ @data - 3@ @data - 4@ @data - 5@ @data - 6@ @data - 7@ ...

weixin_30670965的博客 914

深入解析以太网接口中的BOB Smith电路设计

本文深入解析以太网接口中的BOB Smith电路设计,详细介绍了其在共模噪声抑制、浪涌防护和EMI优化中的关键作用。通过实际案例和设计要点,帮助工程师正确选择75Ω电阻和高压电容等元件,优化PCB布局,解决电压型与电流型PHY的设计差异,以及千兆与百兆以太网的特殊考量。

weixin_28339967的博客 688

华为IC岗笔试刷题含答案(2)

又做了几套题,记录了部分及答案,如有错误,欢迎评论指正。

Ingrid的博客 1万+

LOJ 2288「THUWC 2017」大葱的神力

LOJ 2288「THUWC 2017」大葱的神力 Link Solution 比较水的提交答案题了吧 第一个点爆搜 第二个点爆搜+剪枝,我的剪枝就是先算出 \(mx[i]\) 表示选取第 \(i \sim n\) 个物品所能达到的最大价值,如果当前价值加上后面一段的最大价值都打不到当前最大答案,那么返回 第三个点只有一个包,直接背包 \(f[i][j]\) 表示前 \(i\) 个物品占...

aozhuan8489的博客 677

Codex接入CCSwitch:本地代理与模型路由配置指南

在AI编程工具链中,本地代理与模型路由是连接智能体与模型服务的核心桥梁。Codex作为OpenAI推出的编程智能体,虽然具备强大的仓库理解和自主编码能力,但其默认模型链路固定,导致在多模型切换、内网统一管理及成本控制上存在天然瓶颈。CCSwitch正是为解决这一痛点而生的本地代理工具,它通过OpenAI兼容协议将Codex的请求透明转发至DeepSeek、通义千问等第三方模型服务,使模型切换如同点击按钮般简单。本文从本地代理和模型路由器的基础原理切入,系统讲解Codex与CCSwitch的安装配置、模型ID

weixin_33910460的博客 427

THUWC2017 酱油记

从绍一滚到了学军,,持续滚粗之旅。 day -1 辗转到了绍一(1h的公交好累啊),已经19:00+了,然后进入学军报道,去了一趟寝室感觉好大啊,比某镇上的中学高到不知道哪里去了。当年ZJOI看宣传片的时候以为是ps的,现在发现居然是真的这么好。 于是20:00左右噎了晚饭,可惜由于肚子不舒服不能噎很多。。。qaq 晚上终于可以洗澡了,浑身舒爽。 day 1 5:00被冻醒,被

lych的博客 1898

全网最强AI辅助课题申报教程!超详细流程拆解,助你快速提高课题申报成功率(附各部分高效提示词)

实验法要详细说明自变量、因变量、实验类型、实验过程、实验步骤、测量工具名称、被试取样方式、无关变量控制方法。这个部分就是让评审专家相信我们有能力把这个项目干成,写的时候要重点突出两块内容:第一是研究基础,包括你或团队在相关领域已有的研究成果,比如发表的论文、承担过的课题、获得过的奖励、相关研究经验等,要具体、有说服力;课题申报专门给我们留了一小节用来突出课题的创新之处,不管是研究角度、研究对象、研究方法,还是应用价值都可以提炼作为我们的创新点,最后的语气成果也要明确具体,且要和课题内容紧密相关。

智能AI让学术科研更容易 665

爆肝整理:微信公众号文章HTML编写完整规范,运营必备手册

微信公众号文章排版总是出错?📱 这份完整的HTML编写规范文档帮你彻底解决!文档包含所有支持的标签、CSS属性、实用代码模板和注意事项 📋,让你快速掌握微信文章的HTML编写技巧,告别第三方编辑器依赖 ✅

XiaoqiangClub的博客 2307

# 清北冬令营真题泛做

清北冬令营真题泛做 前言 这段时间为了准备冬令营把清北冬令营真题都做了一下。更个博回顾一下(免得你们老说我咕咕咕)。 先写良心PKU的题再写THU的题, 主要是THU的题和PKU比起来真的毒瘤好多...... PKUWC2018 [PKUWC2018]Minimax 一个比较显然的暴力是归并排序,每次直接前后缀计算答案即可。 为啥不用线段树合并代替归并排序呢? 暴力线段树合并,合并的过程...

df4516的博客 568

UG NX二次开发(C#)-建模-获取圆柱面信息(轴向、半径、深度)

获取圆柱面的信息前言一、在UG NX中面的类型有哪些?二、获取圆柱面信息的函数1.获取半径和轴向2、计算圆柱面的深度值3、测试用例总结 文章目录前言一、在UG NX中面的类型有哪些?二、获取圆柱面信息的函数1.获取半径和轴向2、计算圆柱面的深度值3、测试用例总结 前言 在一个项目中,需要遍历曲面,获得所有的圆柱面和圆柱面的信息,包括轴向、半径和圆柱面的深度。经过研究,采用uFModl.AskFaceData(face.Tag, out faceTypeInt, point, dir, box, out

GimiGimmy 4145

LOJ #2290(「THUWC 2017」随机二分图-状态压缩dp)

某人在玩一个非常神奇的游戏。这个游戏中有一个左右各 n (≤15\le 15)个点的二分图,图中的边会按照一定的规律随机出现。 为了描述这些规律,某人将这些边分到若干个组中。每条边或者不属于任何组 (这样的边一定不会出现),或者只属于一个组。 有且仅有以下三类边的分组: 这类组每组只有一条边,该条边恰好有 50% 的概率出现。 这类组每组恰好有两条边,这两条边有

nike0good |Oier&ACMer | 熟能生巧 926

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

THUWC2017

100+20+20=140 还是很菜... T1 在美妙的数学王国中畅游 一棵树每个点有一个函数(sin,exp,一次函数),支持加边,删边,单点修改,查询一条路径在 $x$ 处的点值和 sol: 题面都给了泰勒展开...那就展吧 展 10 项就可以过,因为时限很松,展 10~20 都是可以的,LCT 维护一下 T2 随机二分图 有一个二分图,三种边对 1.A 边...

245

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

thuwc2017滚粗记

开幕讲话一言不合讲到恋爱很滑稽啊? 试机题目非常因吹斯听啊? 考试题目怎么有点妙啊? 第一题怎么提示里给了泰勒展开公式啊? 那我们把各种函数强行泰勒展开就可以很容易的维护了啊? 那就是一个裸lct了啊? 那我要是A了第一题岂不是美滋滋? 我怎么lct也码挂了啊? 感觉不一定有希望调出来啊? 不如码个线段树拿拿55分? 第二题感觉是个很高明的计数题啊? 后来果然是陈老师的题啊?

jr_mz的博客 1115

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

[LOJ#2290][THUWC 2017][概率][状压][DP][陈老师神题]随机二分图

又是一个冬令营未填的坑可以根据期望的直接定义,求出每个完美匹配出现的概率。 因为n很小,可以状压DP,fs,tf_{s,t}表示左边被匹配的状态为ss,右边被匹配的状态为tt时的概率,因为左右边被匹配的点个数是相同的,所以状态数大概为∑Cin2\sum {C^i_n}^2???但是肯定远远比这个小。然后直接做不太容易实现 可以把两条边的组拆分。 同时出现的组 可以拆成两条50%的边,以及25%

CE玩家 1736

THUWC2017 瞎搞记

DAY-1 上午去WC的社会实践搞搞搞 晚上和昌老师,jkxing还有fzw一起坐着大巴去学车 报了一会道进了宿舍 卧槽这个宿舍怎么这么高级! 门是刷卡的! 各种开关是触控的! 每个屋配淋浴间和厕所阳台,两个手盆! 有插座和台灯桌子! 有wifi!虽然连不上-_- 比之前住过的宿舍都不知高到哪里去了 认识了一下同寝的四位神犇,全是四川的,象征性地问了一下名字,反正记不住-_-

neither_nor 2783

【LOJ #2290】「THUWC 2017」随机二分图(状压DP)

传送门 暴力的做法是设f[s1][s2]f[s1][s2]f[s1][s2]为左右匹配状态转移 考虑对于第二类 拆成两条独立的,出现概率为50%50\%50%的边 但是一起出现的概率少了25%25\%25% 可以再加一个一起出现的25%25\%25%的情况 第三类类似 加一个一起出现为−25%-25\%−25%的情况 然后暴力dpdpdp,用mapmapmap存状态 每次匹配标号最小的一个点 这样...

Stargazer的博客 251

WC2017 && THUWC2017 游记

begin 放假前几天,突然被老师叫到办公室,告诉我清华的冬令营给了我一个参与的名额。记得同一年的5月份,高二的学长Manchery参与了清华的夏令营,不负众望,他获得了清华大学高考无条件降60分的政策,我知道我没有他的实力,但也有一点无知的幻想,幻想我能顺利度过笔试、面试,多多少少得到一些能够炫耀的成绩。 于是整个寒假就只有五天在家过年,其他时间都在学校,准备这两个冬令营。杜老师、吉利给我们

CE玩家 1677

[TravelNotes] WC 2017 & THUWC 2017 游记

Pre() 春节在家 赶紧临时抱佛脚一发 学习杜教筛和SAM 刷刷模板题 水水水 还想起自己数据结构的姿势水平不高啊 恶补了一下 替罪羊树 线段树分裂 二进制分组 然后重新学习了LCT的姿势 再看看自己以前码的LCT真是丑的不行 某些天晚些时候 恰好有比赛 就打了打CF BC 还有 Goodbye Bingshen 某天被dls安利到vjudge打了一场ABC53/ARC68的复现赛 被d...

雯舞 2452

WC2017咸鱼记&THUWC2017垫底记

DAY -3来到杭州 下午一直和张棍格斗 晚上走了n久找不到饭吃,还冻成了dogDAY -2早上模拟赛因为账号权限没了所以啥都不记得了 讨论完试题只有1h给我们洗澡/吃饭 洗完澡去买了个汉堡 晚上继续做模拟赛,假老师的题贼毒,我20分就是高分选手DAY -1早上做的全程倦生,因为肚子好疼啊 这个持续了一天,估计是冷的原因 晚上可以自由做题就去刷了一下luogu月赛。DAY 0早上继续做

不来也不去的一只失忆蝴蝶 3591

WC2017&THUWC2017 游记

我可能去了假的WC QAQ 人世几回伤往事,山形依旧枕寒流。day1报到日,然而我们学校从重庆坐火车去绍兴,完美错过开幕式day2上午鏼鏼鏼讲字符串,勉强听懂前半截。 下午rzz的猜数游戏,尛焱轟讲IOI2016试题,勉强跟上节奏。 从晚上的营员交流开始,冬眠营正式拉开序幕。 myy的Berlekamp Massey算法,从头开始懵逼 敦敦敦的30KB神题彻底将我催眠,接下来的仙人掌计数在

LiuJunhao's Blog 3051

【THUWC2017】随机二分图(状压)(容斥)(记忆化搜索)

传送门 题解: 首先算期望就是对于每一种完美匹配算出现概率然后求个和。 如果只有第000类边的话我们可以考虑如下的DP,把现在左侧还没有匹配的点压起来,右侧还没有匹配的点压起来,用一个int表示状态。 那么显然我们需要知道的就是这些点自己期望能够产生多少种合法匹配使得自己匹配完。 由于边是无序的,转移的时候直接强行枚举一边把集合中编号最小的点搞掉即可。 考虑111类边组和222类边组。 这两类的...

zxyoi_dreamer的博客(不定期诈尸) 193
上一篇: 题解:P9176 [COCI2022-2023#4] Vrsta
下一篇: Claude3 正式发布,支持多模态(附注册使用教程)
Chat_lsm2024
博客等级 码龄7年 903粉丝 79原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值