HDU 3001 Travelling

转载注明出处:http://blog.youkuaiyun.com/u013480600/article/details/19832395       感谢大佬

After coding so many days,Mr Acmer wants to have a good rest.So travelling is the best choice!He has decided to visit n cities(he insists on seeing all the cities!And he does not mind which city being his start station because superman can bring him to any city at first but only once.), and of course there are m roads here,following a fee as usual.But Mr Acmer gets bored so easily that he doesn't want to visit a city more than twice!And he is so mean that he wants to minimize the total fee!He is lazy you see.So he turns to you for help.
Input
There are several test cases,the first line is two intergers n(1<=n<=10) and m,which means he needs to visit n cities and there are m roads he can choose,then m lines follow,each line will include three intergers a,b and c(1<=a,b<=n),means there is a road between a and b and the cost is of course c.Input to the End Of File.
Output
Output the minimum fee that he should pay,or -1 if he can't find such a route.
Sample Input
2 1
1 2 100
3 2
1 2 40
2 3 50
3 3
1 2 3
1 3 4
2 3 10
Sample Output
100
90
7 

//经典好题:三进制旅行商问题
/*
现在有一个具有n个顶点和m条边的无向图(每条边都有一个距离权值),
小明可以从任意的顶点出发,他想走过所有的顶点而且要求走的总距
离最小,并且他要求过程中走过任何一个点的次数不超过2次。
输入:多组实例。每个实例第一行为n(1<=n<=10)和m,接下来m行对m条
边描述,每行包括a,b(1<=a,b<=n)和c表示节点a和b之间有一条长c的路。
输出:输出他需要走的最短距离,如果不存在这样的路,输出-1.


如果本题没有说任意一个点走过的次数不超过2这个条件,那么
本题可以用类似TSP问题的解法即可,即用d[i][S]=x,表示当前
人在i点,并且走过了集合S中的所有点,所行走的最小距离为x。
d[i][S]=min{ d[j][S-i]+min_dist[j][i] } j,i属于S。但是
现在要求点出现的次数<=2,所以要把点出现的次数也纳入到d
的第二维下标(即S)中去。 令d[i][S]=x,表示当前人在i点,
并且走过了集合S(用S的三进制表示点出现次数的集合)中的
所有点以及对应点出现的次数满足S集合时,所行走的最小距离为x。
状态转移方程是:d[i][S]=min{ d[j][S1]+dist[j][i] } ,S与S1
的关系是S的三进制形式除了一位减一之外(从2变1或从1变0)
其他所有位都与S1的三进制数对应位相同,j在S1集合中出现的
次数属于[1,2],i在S1集合中出现的次数属于[0,1],dist[i][j]
表示节点i与j之间的初始距离(不是最小距离,如果从i到j
没有边则不能执行该状态转移)。初值为:d[i][(3^i)]=0,
其他为-1.(本题顶点的标号,题目说是1到n,我们程序中用
0到n-1处理。最终我们所求为:max{ d[i][S] },0<=i<=n-1。
S的三进制形式中没有一个0(最多2次是通过状态转移方程来
限制住的)。
*/ 
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;

int p[11];
int n, m, mn;
int dis[11][11];
int dp[11][59050];
int state[11][59050]; //表示状态S的第i位 
void init() { //初始化计算p[i] 为3的i次方, 
	p[0] = 1;
	for(int i = 1; i <= 10; i++) {
		p[i] = p[i-1]*3;
	}
	for(int i = 0; i <= p[10]; i++) {
		int t = i, k = 0;
		while(t) {
			state[k++][i] = t%3;
			t /= 3;
		}
	}
}
void init1() {
	for(int i = 0; i <= n; i++) {
		for(int j = 0; j <= n; j++) {
			if(i != j) dis[i][j] = inf;
			else dis[i][j] = 0;
		}
	}
}
int main() {
	init();
	int a, b, c;
	while(~scanf("%d%d",&n,&m)) {
		init1();
		mn = inf;
		for(int i = 0; i < m; i++) {
			scanf("%d%d%d",&a,&b,&c);
			a--;b--;
			if(dis[a][b] > c)
			dis[a][b] = dis[b][a] = c;
		}
		memset(dp, inf, sizeof(dp));
		for(int i = 0; i < n; i++)
		dp[i][p[i]] = 0;//初始化dp,只有i访问,费用为0 
		for(int S = 0; S < p[n]; S++) {
			int sum = 1;//遍历的点记录 
			for(int i = 0; i < n; i++) {
				if(state[i][S] == 0) sum = 0;
				if(dp[i][S] == inf) continue;
				for(int k = 0; k < n; k++) {//枚举下一个点 
					if(i == k) continue;
					//表示不存在边,或者这种状态已经被访问两次以上 
					if(dis[i][k] == inf || state[k][S] >= 2)
					continue;
					int ans = S + p[k]; //表示此时状态 
					dp[k][ans] = min(dp[k][ans], dp[i][S] + dis[i][k]);
				}
			}
			if(sum) {
				for(int i = 0; i < n; i++) {
					mn = min(mn , dp[i][S]);
				}
			}
		}
		if(mn == inf) mn = -1;
		printf("%d\n",mn);
	}
	return 0;
}



六、状压DP的优化技巧 6.1 预处理合法状态 很多问题中,大部分状态是不合法的,可以预先筛选: cpp vector valid_states; for (int state = 0; state < (1 << n); ++state) { if (check(state)) { // 检查state是否合法 valid_states.push_back(state); } } 6.2 滚动数组优化 当状态只依赖前一个阶段时,可以节省空间: cpp vector<vector> dp(2, vector(size)); // 只保留当前和上一个状态 int now = 0, prev = 1; for (int i = 1; i <= n; ++i) { swap(now, prev); for (auto& state : valid_states) { dp[now][state] = 0; // 清空当前状态 // 状态转移… } } 6.3 记忆化搜索实现 有时递归形式更直观: cpp int memo[1<<20][20]; // 记忆化数组 int dfs(int state, int u) { if (memo[state][u] != -1) return memo[state][u]; // 递归处理… return memo[state][u] = res; } 七、常见问题与调试技巧 7.1 常见错误 位运算优先级:总是加括号,如(state & (1 << i)) 数组越界:状态数是2ⁿ,不是n 初始状态设置错误:比如TSP中dp[1][0] = 0 边界条件处理不当:如全选状态是(1<<n)-1,不是1<<n 7.2 调试建议 打印中间状态:将二进制状态转换为可视化的形式 cpp void printState(int state, int n) { for (int i = n-1; i >= 0; --i) cout << ((state >> i) & 1); cout << endl; } 从小规模测试用例开始(如n=3,4) 使用assert检查关键假设 八、学习路线建议 初级阶段: 练习基本位操作 解决简单状压问题(如LeetCode 464、526题) 中级阶段: 掌握经典模型(TSP、棋盘覆盖) 学习优化技巧(预处理、滚动数组) 高级阶段: 处理高维状压(如需要同时压缩多个状态) 结合其他算法(如BFS、双指针) 九、实战练习题目推荐 入门题: LeetCode 78. Subsets(理解状态表示) LeetCode 464. Can I Win(简单状压DP) 中等题: LeetCode 526. Beautiful Arrangement LeetCode 691. Stickers to Spell Word 经典题: POJ 2411. Mondriaan’s Dream(棋盘覆盖) HDU 3001. Travelling(三进制状压) 挑战题: Codeforces 8C. Looking for Order Topcoder SRM 556 Div1 1000. LeftRightDigitsGame2 记住,掌握状压DP的关键在于: 彻底理解二进制状态表示 熟练运用位运算 通过大量练习培养直觉 希望这份超详细的教程能帮助你彻底掌握状压DP!如果还有任何不明白的地方,可以针对具体问题继续深入探讨。 请帮我转成markdown语法输出,谢谢
08-13
欧姆龙FINS(工厂集成网络系统)协议是专为该公司自动化设备间数据交互而设计的网络通信标准。该协议构建于TCP/IP基础之上,允许用户借助常规网络接口执行远程监控、程序编写及信息传输任务。本文档所附的“欧ronFins.zip”压缩包提供了基于C与C++语言开发的FINS协议实现代码库,旨在协助开发人员便捷地建立与欧姆龙可编程逻辑控制器的通信连接。 FINS协议的消息框架由指令头部、地址字段、操作代码及数据区段构成。指令头部用于声明消息类别与长度信息;地址字段明确目标设备所处的网络位置与节点标识;操作代码定义了具体的通信行为,例如数据读取、写入或控制器指令执行;数据区段则承载实际交互的信息内容。 在采用C或C++语言实施FINS协议时,需重点关注以下技术环节: 1. **网络参数设置**:建立与欧姆龙可编程逻辑控制器的通信前,必须获取控制器的网络地址、子网划分参数及路由网关地址,这些配置信息通常记载于设备技术手册或系统设置界面。 2. **通信链路建立**:通过套接字编程技术创建TCP连接至控制器。该过程涉及初始化套接字实例、绑定本地通信端口,并向控制器网络地址发起连接请求。 3. **协议报文构建**:依据操作代码与目标功能构造符合规范的FINS协议数据单元。例如执行输入寄存器读取操作时,需准确配置对应的操作代码与存储器地址参数。 4. **数据格式转换**:协议通信过程中需进行二进制数据的编码与解码处理,包括将控制器的位状态信息或数值参数转换为字节序列进行传输,并在接收端执行逆向解析。 5. **异常状况处理**:完善应对通信过程中可能出现的各类异常情况,包括连接建立失败、响应超时及错误状态码返回等问题的处理机制。 6. **数据传输管理**:运用数据发送与接收函数完成信息交换。需注意FINS协议可能涉及数据包的分割传输与重组机制,因单个协议报文可能被拆分为多个TCP数据段进行传送。 7. **响应信息解析**:接收到控制器返回的数据后,需对FINS响应报文进行结构化解析,以确认操作执行状态并提取有效返回数据。 在代码资源包中,通常包含以下组成部分:展示连接建立与数据读写操作的示范程序;实现协议报文构建、传输接收及解析功能的源代码文件;说明库函数调用方式与接口规范的指导文档;用于验证功能完整性的测试案例。开发人员可通过研究这些材料掌握如何将FINS协议集成至实际项目中,从而实现与欧姆龙可编程逻辑控制器的高效可靠通信。在工程实践中,还需综合考虑网络环境稳定性、通信速率优化及故障恢复机制等要素,以确保整个控制系统的持续可靠运行。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值