Salesmen - UVa 1424 dp

本文探讨了韩国知名互联网公司NHN的销售人员如何通过定期报告位置信息来优化工作路径,利用图论知识确保路径的有效性和效率。文章详细介绍了如何在给定的路径中寻找最优的、符合工作区域图结构的路径,以及计算不同路径之间的距离,从而实现路径的最优化。



Traveling salesmen of nhn. (the prestigious Korean internet company) report their current location to the company on a regular basis. They also have to report their new location to the company if they are moving to another location. The company keep each salesman's working path on a map of his working area and uses this path information for the planning of the next work of the salesman. The map of a salesman's working area is represented as a connected and undirected graph, where vertices represent the possible locations of the salesman an edges correspond to the possible movements between locations. Therefore the salesman's working path can be denoted by a sequence of vertices in the graph. Since each salesman reports his position regularly an he can stay at some place for a very long time, the same vertices of the graph can appear consecutively in his working path. Let a salesman's working path be correct if two consecutive vertices correspond either the same vertex or two adjacent vertices in the graph.

For example on the following graph representing the working area of a salesman,

\epsfbox{p4256.eps}

a reported working path [1 2 2 6 5 5 5 7 4] is a correct path. But a reported working path [1 2 2 7 5 5 5 7 4] is not a correct path since there is no edge in the graph between vertices 2 a 7. If we assume that the salesman reports his location every time when he has to report his location (but possibly incorrectly), then the correct path could be [1 2 2 4 5 5 5 7 4], [1 2 4 7 5 5 5 7 4], or [1 2 2 6 5 5 5 7 4].

The length of a working path is the number of vertices in the path. We define the distance between two paths Aa1a2...an and B = b1b2...bn of the same length n as

dist(AB) = $\displaystyle \sum^{​{n}}_{​{i=1}}$d (aibi)

where

d (ab) = $\displaystyle \left\{\vphantom{ \begin{array}{cc} 0 & \mbox{if } a=b \\  1 & \mbox{otherwise} \end{array} }\right.$$\displaystyle \begin{array}{cc} 0 & \mbox{if } a=b \\  1 & \mbox{otherwise} \end{array}$

Given a graph representing the working area of a salesman and a working path (possible not a correct path), A , of a salesman, write a program to compute a correct working path, B , of the same length where the distancedist(AB) is minimized.

Input 

The program is to read the input from standard input. The input consists of T test cases. The number of test cases (T) is given in the first line of the input. The first line of each test case contains two integers n1 ,n2 (3$ \le$n1$ \le$100, 2$ \le$n2$ \le$4, 950) where n1 is the number of vertices of the graph representing the working map of a salesman and n2 is the number of edges in the graph. The input graph is a connected graph. Each vertex of the graph is numbered from 1 to n1 . In the following n2 lines, each line contains a pair of vertices which represent an edge of the graph. The last line of each test case contains information on a working path of the salesman. The first integer n (2$ \le$n$ \le$200) in the line is the length of the path and the following n integers represent the sequence of vertices in the working path.

Output 

Your program is to write to standard output. Print one line for each test case. The line should contain the minimum distance of the input path to a correct path of the same length.

Sample Input 

2 
7 9 
1 2 
2 3 
2 4 
2 6 
3 4 
4 5 
5 6 
7 4 
7 5 
9 1 2 2 7 5 5 5 7 4 
7 9 
1 2 
2 3 
2 4 
2 6 
3 4 
4 5 
5 6 
7 4 
7 5 
9 1 2 2 6 5 5 5 7 4

Sample Output 

1 
0

题意:找一个可以在图上前行的序列,使得其与给定序列相差的数字数量最小,输出这个最小的值。

思路:dp[i][j]表示已经匹配好前i个序列并且最后停在j点时的最小权值。

AC代码如下:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> vc[110];
int val[210],len[110],dp[210][110],INF=1e9;
int main()
{
    int t,T,n,m,i,j,k,u,v,length,f,ans;
    scanf("%d",&T);
    for(t=1;t<=T;t++)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
           vc[i].clear();
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            vc[u].push_back(v);
            vc[v].push_back(u);
        }
        for(i=1;i<=n;i++)
        {
            vc[i].push_back(i);
            len[i]=vc[i].size();
        }

        scanf("%d",&length);
        for(i=1;i<=length;i++)
            scanf("%d",&val[i]);
        for(i=1;i<=n;i++)
           if(i==val[1])
             dp[1][i]=0;
           else
             dp[1][i]=1;
        for(i=2;i<=length;i++)
        {
            for(u=1;u<=n;u++)
            {
                dp[i][u]=INF;
                if(u==val[i])
                  f=0;
                else
                  f=1;
                for(k=0;k<len[u];k++)
                {
                    v=vc[u][k];
                    dp[i][u]=min(dp[i][u],dp[i-1][v]+f);
                }
            }
        }
        ans=INF;
        for(i=1;i<=n;i++)
           ans=min(ans,dp[length][i]);
        printf("%d\n",ans);
    }
}



#include <stdio.h> #include <stdlib.h> #include <string.h> #include <conio.h> #include <windows.h> #define MAX_SALESMEN 4 #define MAX_PRODUCTS 5 #define MAX_DAYS 30 #define FILENAME "sales_data.dat" // 销售记录结构体 typedef struct { int salesman_id; int product_id; double amount; } SaleRecord; // 销售员汇总结构体 typedef struct { int salesman_id; double total_sales; double product_sales[MAX_PRODUCTS]; // 每种产品的销售额 } SalesmanSummary; // 产品汇总结构体 typedef struct { int product_id; double total_sales; } ProductSummary; // 全局变量 SaleRecord records[MAX_DAYS * MAX_SALESMEN]; // 最大记录数 int record_count = 0; SalesmanSummary salesman_summary[MAX_SALESMEN]; ProductSummary product_summary[MAX_PRODUCTS]; // 函数声明 void initSystem(); void mainMenu(); void inputRecords(); void calculateSales(); void sortSalesmen(); void sortProducts(); void displaySalesmen(); void displayProducts(); void saveData(); void loadData(); void clearScreen(); void setColor(int color); void centerText(const char *text); void drawHeader(const char *title); void drawTableHeader(); void drawFooter(); int main() { // 设置控制台窗口大小 system("mode con cols=80 lines=30"); // 初始化系统 initSystem(); // 加载数据 loadData(); // 显示主菜单 mainMenu(); return 0; } // 初始化系统 void initSystem() { // 初始化销售员汇总 for (int i = 0; i < MAX_SALESMEN; i++) { salesman_summary[i].salesman_id = i + 1; salesman_summary[i].total_sales = 0.0; for (int j = 0; j < MAX_PRODUCTS; j++) { salesman_summary[i].product_sales[j] = 0.0; } } // 初始化产品汇总 for (int i = 0; i < MAX_PRODUCTS; i++) { product_summary[i].product_id = i + 1; product_summary[i].total_sales = 0.0; } } // 主菜单 void mainMenu() { int choice; do { clearScreen(); setColor(14); // 黄色 // 绘制菜单框 printf("\n"); centerText("===================================================="); centerText("| 销售管理系统 - 主菜单 |"); centerText("===================================================="); centerText("| |"); centerText("| 1. 录入便条信息 |"); centerText("| 2. 计算每人每种产品的销售额 |"); centerText("| 3. 按销售额对销售员进行排序 |"); centerText("| 4. 按销售额对产品进行排序 |"); centerText("| 5. 显示销售员销售额 |"); centerText("| 6. 显示产品销售额 |"); centerText("| 7. 退出系统 |"); centerText("| |"); centerText("===================================================="); setColor(11); // 浅蓝色 centerText("当前销售记录数: "); printf("%d\n", record_count); setColor(12); // 红色 centerText("请选择操作 (1-7): "); // 获取用户选择 scanf("%d", &choice); switch (choice) { case 1: inputRecords(); break; case 2: calculateSales(); break; case 3: sortSalesmen(); break; case 4: sortProducts(); break; case 5: displaySalesmen(); break; case 6: displayProducts(); break; case 7: saveData(); clearScreen(); centerText("感谢使用销售管理系统!"); centerText("按任意键退出..."); getch(); exit(0); default: setColor(12); centerText("无效选择! 请重新输入."); getch(); } } while (choice != 7); } // 录入便条信息 void inputRecords() { clearScreen(); drawHeader("录入便条信息"); setColor(11); // 浅蓝色 centerText("每天需要录入4位销售员的便条信息"); centerText("--------------------------------"); int day = (record_count / MAX_SALESMEN) + 1; printf("\n"); centerText("正在录入第"); printf("%d", day); centerText("天的销售数据..."); for (int i = 0; i < MAX_SALESMEN; i++) { if (record_count >= MAX_DAYS * MAX_SALESMEN) { setColor(12); // 红色 centerText("已达到最大记录数! 无法继续录入."); getch(); return; } SaleRecord record; int valid = 0; do { printf("\n"); centerText("-- 销售员"); printf("%d", i + 1); centerText(" --"); setColor(15); // 白色 centerText("销售员代号 (1-4): "); scanf("%d", &record.salesman_id); centerText("产品代号 (1-5): "); scanf("%d", &record.product_id); centerText("销售额: "); scanf("%lf", &record.amount); // 验证输入 if (record.salesman_id < 1 || record.salesman_id > MAX_SALESMEN) { setColor(12); centerText("销售员代号无效! 请输入1-4."); valid = 0; } else if (record.product_id < 1 || record.product_id > MAX_PRODUCTS) { setColor(12); centerText("产品代号无效! 请输入1-5."); valid = 0; } else if (record.amount <= 0) { setColor(12); centerText("销售额必须大于0!"); valid = 0; } else { valid = 1; } } while (!valid); // 添加到记录数组 records[record_count++] = record; } // 保存数据 saveData(); setColor(10); // 绿色 centerText("数据录入成功! 按任意键返回..."); getch(); } // 计算每人每种产品的销售额 void calculateSales() { // 重置汇总数据 initSystem(); // 遍历所有记录 for (int i = 0; i < record_count; i++) { SaleRecord record = records[i]; int salesman_idx = record.salesman_id - 1; int product_idx = record.product_id - 1; // 更新销售员汇总 salesman_summary[salesman_idx].total_sales += record.amount; salesman_summary[salesman_idx].product_sales[product_idx] += record.amount; // 更新产品汇总 product_summary[product_idx].total_sales += record.amount; } setColor(10); // 绿色 centerText("销售额计算完成! 按任意键返回..."); getch(); } // 按销售额对销售员进行排序 void sortSalesmen() { // 使用冒泡排序 for (int i = 0; i < MAX_SALESMEN - 1; i++) { for (int j = 0; j < MAX_SALESMEN - i - 1; j++) { if (salesman_summary[j].total_sales < salesman_summary[j + 1].total_sales) { // 交换销售员汇总 SalesmanSummary temp = salesman_summary[j]; salesman_summary[j] = salesman_summary[j + 1]; salesman_summary[j + 1] = temp; } } } setColor(10); // 绿色 centerText("销售员排序完成! 按任意键返回..."); getch(); } // 按销售额对产品进行排序 void sortProducts() { // 使用冒泡排序 for (int i = 0; i < MAX_PRODUCTS - 1; i++) { for (int j = 0; j < MAX_PRODUCTS - i - 1; j++) { if (product_summary[j].total_sales < product_summary[j + 1].total_sales) { // 交换产品汇总 ProductSummary temp = product_summary[j]; product_summary[j] = product_summary[j + 1]; product_summary[j + 1] = temp; } } } setColor(10); // 绿色 centerText("产品排序完成! 按任意键返回..."); getch(); } // 显示销售员销售额 void displaySalesmen() { clearScreen(); drawHeader("销售员销售额汇总"); // 显示表头 setColor(14); // 黄色 printf("\n"); centerText("销售员 | 总销售额 | 产品1 | 产品2 | 产品3 | 产品4 | 产品5"); centerText("-----------------------------------------------------------------"); // 显示每个销售员的数据 setColor(11); // 浅蓝色 for (int i = 0; i < MAX_SALESMEN; i++) { printf(" %d |", salesman_summary[i].salesman_id); printf(" %8.2f |", salesman_summary[i].total_sales); for (int j = 0; j < MAX_PRODUCTS; j++) { printf(" %6.2f |", salesman_summary[i].product_sales[j]); } printf("\n"); } drawFooter(); setColor(12); // 红色 centerText("按任意键返回..."); getch(); } // 显示产品销售额 void displayProducts() { clearScreen(); drawHeader("产品销售额汇总"); // 显示表头 setColor(14); // 黄色 printf("\n"); centerText("产品 | 总销售额 "); centerText("---------------"); // 显示每个产品的数据 setColor(11); // 浅蓝色 for (int i = 0; i < MAX_PRODUCTS; i++) { printf(" %d |", product_summary[i].product_id); printf(" %8.2f \n", product_summary[i].total_sales); } drawFooter(); setColor(12); // 红色 centerText("按任意键返回..."); getch(); } // 保存数据到文件 void saveData() { FILE *file = fopen(FILENAME, "wb"); if (file) { // 保存记录数 fwrite(&record_count, sizeof(int), 1, file); // 保存所有记录 fwrite(records, sizeof(SaleRecord), record_count, file); // 保存销售员汇总 fwrite(salesman_summary, sizeof(SalesmanSummary), MAX_SALESMEN, file); // 保存产品汇总 fwrite(product_summary, sizeof(ProductSummary), MAX_PRODUCTS, file); fclose(file); } } // 从文件加载数据 void loadData() { FILE *file = fopen(FILENAME, "rb"); if (file) { // 加载记录数 fread(&record_count, sizeof(int), 1, file); // 加载所有记录 fread(records, sizeof(SaleRecord), record_count, file); // 加载销售员汇总 fread(salesman_summary, sizeof(SalesmanSummary), MAX_SALESMEN, file); // 加载产品汇总 fread(product_summary, sizeof(ProductSummary), MAX_PRODUCTS, file); fclose(file); } } // 清除屏幕 void clearScreen() { system("cls"); } // 设置文本颜色 void setColor(int color) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color); } // 居中显示文本 void centerText(const char *text) { int len = strlen(text); int padding = (80 - len) / 2; for (int i = 0; i < padding; i++) printf(" "); printf("%s\n", text); } // 绘制标题 void drawHeader(const char *title) { setColor(14); // 黄色 centerText("===================================================="); centerText("| 销售管理系统 |"); centerText("===================================================="); centerText("| |"); printf("|%*s%*s|\n", (80 - strlen(title) - 2) / 2 + strlen(title), title, (80 - strlen(title) - 2) / 2, ""); centerText("| |"); centerText("===================================================="); } // 绘制表尾 void drawFooter() { setColor(14); // 黄色 centerText("===================================================="); } 帮我分析上述代码
最新发布
07-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值