【算法每日一练]-动态规划(保姆级教程 篇13)POJ2686马车旅行 #POJ3254 玉米田 #POJ1185:炮兵阵地

本文主要是介绍【算法每日一练]-动态规划(保姆级教程 篇13)POJ2686马车旅行 #POJ3254 玉米田 #POJ1185:炮兵阵地,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

今天知识点

dp每个票的使用情况,然后更新此票状态下的最优解,dp到没有票就行了

dp每行的种植状态,从i-1行进行不断转移

dp每行的种植状态,从i-1和i-2行进行不断转移

POJ2686马车旅行

思路:

POJ3254 玉米田

思路:

POJ1185:炮兵阵地

思路:


        

        

前置知识:

基于状态压缩下的集合操作:
1.空集:                                                               0

2.只含有第i个元素的集合{i}:                          1<<i

3.含有全部n个元素的集合{0,1,2,....,n-1}:  (1<<n)-1

4.判断第i个元素是否属于集合S:            if(S>>i&1)

5.向集合中加入第i个元素S ∪ {i}:                S|1<<i

6.从集合中除去第i个元素S - {i}:            S&~(1<<i)

7.集合S和T的并集S∪T:                                 S | T

8.集合S和T的交集S∩T:                                S & T

        

        

POJ2686马车旅行

有一个公路网连接这些城市,可以乘坐马车通行。乘坐马车需要一张票,旅行者有许多车票,每张票上都标记了马的数量,马越多跑的越快。
你应该考虑如果使用这些票使得在最短时间内把旅行者从出发点他带到目的地的最佳路线。
假设一下条件:
1.通过公路直接连接的两个城市之间只能使用一张车票,且每张票只能用一次
2.乘马车的时间等于两城市之间的距离除以马的数量
3.忽略换乘所需的时间

输入:
2 4 4 2 1
3 1
2 3 3
1 3 3
4 1 2
4 2 5
2 4 3 4 1
5 5
1 2 10
2 3 10
3 4 10
输出:
3.667
impossible

        

思路:

因为每跑一次票的状态就变动一次,所以我们设置dp[s][u]表示达到当前u点且持有s车票的最小花费,其中s是票的二进制状态。

        

状态转移:从u到v,当前点v的状态s一定最小的dp[s'][u]+dis/t转移过来(其中s=s'&~(1<<i))

        
dp[s&~(1<<i)][v]=min(dp[s&~(1<<i)][v],dp[s][u]+dis[u][v]/t[i])  

        
转移顺序:s从大到小,因为大的状态必须要先于小的先确定下来,所以s一定在最外层。然后是每个起点到每个终点使用每张票来去更新每个点,也就是维护该状态下的最优解
        

#include <bits/stdc++.h>
using namespace std;
const double inf=0x3f3f3f3f;
double ans;
int n,m,p,a,b;
int t[20],dis[50][50];
double dp[1<<10][32];//dp[s][u]表示达到当前u点且持有s车票的最小花费void solve(){for(int i=0;i<(1<<(n+1));i++)for(int j=0;j<=m+1;j++)dp[i][j]=inf;dp[(1<<n)-1][a]=0;//起点状态ans=inf;for(int s=(1<<n)-1;s>=0;s--){//状态从大到小for(int u=1;u<=m;u++)//遍历每个城市for(int i=0;i<n;i++)//遍历每种车票可用就用if((s>>i)&1)for(int v=1;v<=m;v++)//尾点城市if(dis[u][v]>=0){//如果能走,就把第i张票置零dp[s&~(1<<i)][v]=min(dp[s&~(1<<i)][v],dp[s][u]+(double)dis[u][v]/t[i]);}ans=min(ans,dp[s][b]);}} 
int main(){while(cin>>n>>m>>p>>a>>b){if(n+m+p+a+b==0)break;for(int i=0;i<n;i++){scanf("%d",&t[i]);//每张车票的数量}memset(dis,-1,sizeof(dis));//初始化成无穷大也行for(int i=0;i<p;i++){//p条变int u,v,w;scanf("%d%d%d",&u,&v,&w);dis[u][v]=dis[v][u]=w;}solve();if(ans==inf)printf("Impossible\n");else printf("%.3lf\n",ans);}
}

        

        

POJ3254 玉米田

由m*n(m<12,n<12)的方格组成的玉米田,要在这些方格上种上玉米,有些方格是贫瘠的(0表示),有些是肥沃的(1表示),贫瘠的不能种植。
另外在种植的时候不能在相邻的方格种上玉米,也就是不能共享边。问一共有多少种种植方案。

输入
2 3 
1 1 1
0 1 0

        

思路:

每一行的状态都和上一行的状态有关,状态数有太多因此需要进行状态压缩
首先将每行的状态压缩成j的二进制状态。然后我们进行dp行,设置dp[i][j]表示第i行的第j种状态时对应的前i行的方案数。
转移方程:dp[i][j]=(dp[i][j]+dp[i-1][k])%mod; (k是第i-1行所有的可行状态)

在确定每行转移的时候都要考虑:
1.横向方案    2.横向方案是否和地图匹配     3.是否和i-1行冲突

        
存每行的可能状态:相邻的两列不能都是1,那就看x&x<<1是不是0(就是可能的横向方案)
是否和i-1冲突:种表示1,不种表示0 那么在判断两行合法性时,不能出现有一列同1(两行都种),所以x&y=0是合法的
存图:肥沃我们用0表示,贫瘠用1表示  那么判断此地和此种法合法性时,不能出现同1(在贫瘠的地方种),所以x&y=0是合法的
(如果不这样的话0和1与是0,你就分不清了)

        
【注意】:外面每行i循环一次,其次里面是第i行的每个状态j循环一次(找到合适的j),最后是第i-1行的每个状态k循环一次(找到每个合适的k),共O(n^3)

#include <bits/stdc++.h>
using namespace std;
const int mod=1e8;
int sta[600],top,n,m;
int dp[20][600],cur[20];bool check(int x){if(x&x<<1)return 0;return 1;
} void init(){//预处理top=0;for(int i=0;i<(1<<n);i++){//记录所有的没有相邻1的种法if(check(i))sta[++top]=i;}
}void solve(){for(int j=1;j<=top;j++){//处理第一行if(!(sta[j]&cur[1])) dp[1][j]=1;}for(int i=2;i<=m;i++){//处理剩余行for(int j=1;j<=top;j++){//sta[j]是第i行的每种种法if(sta[j]&cur[i]) continue;//检测当前状态是否和当前行匹配for(int k=1;k<=top;k++){//sta[k]是i-1行的每种法if(sta[k]&cur[i-1])continue;//检测当前状态和当前行是否匹配if(sta[j]&sta[k])continue;//第i行和第i-1行有冲突dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;}}}
}int main(){while(cin>>m>>n){//m是行n是列init();int num;memset(dp,0,sizeof(dp));for(int i=1;i<=m;i++){cur[i]=0;for(int j=1;j<=n;j++){scanf("%d",&num);if(num==0)cur[i]+=(1<<(n-j));//读入地图 1变0,0变1}}solve();int ans=0;for(int j=1;j<=top;j++)ans=(ans+dp[m][j])%mod;//最后一行所有方案数加起来cout<<ans;}
}

        

        

        

POJ1185:炮兵阵地

在N*M(N<100,M<10)的地图上布置炮兵,H格子为山地不能布置,P格子为平原可以布置。炮兵的攻击范围是沿横向左右各两格,沿纵向上下个两格子
炮兵之间不能误伤。问在整个地图中最多能拜访多少个炮兵?
5 4
PHPP
PPHH
PPPP
PHPP
PHHP

        

思路:

        
首先要对行进行状态压缩(对列的话太大了,枚举2^100还不如不压缩呢),我们每次确定行的状态都需要考虑:
1.横向方案    2.横向方案是否和地图匹配     3.是否和i-1行i-2行冲突


设置dp[i][j][k]表示第i行为第j状态,第i-1行为第k状态时 对应的前i行放置的最大炮兵数。
转移方程:dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);

(j是第i行的方案,k是第i-1行的方案,t是i-2行的方案)

        
存每行的可能状态:左右相邻1个间隔和2个间隔都不能炮兵(就是可能的横向方案)
存图:(1,1)开始存。0表示平原,1表示山地,那么在放置的时候不能出现同1(在山地放炮兵),所以x&y=0是合法的(保证合法的是0就行了)
是否冲突:第i行和第i-1行,第i-2行 不能出现有一列同1(两行都放炮兵),所以x&y=0是合法的

        
【注意】:外面每行i循环一次,其次里面是第i行的每个状态j循环一次(找到合适的j),然后是第i-1行的每个状态k循环一次(供第i行找到合适的k),
接着是第i-2行的每个状态t循环一次(供第i-1行找到合适的t)

#include <bits/stdc++.h>
using namespace std;
int n,m,top;
char mp[110][20];
int num[70];
int stk[70],cur[70];//stk表示横向可能的方案,cur是我们存的地图行
int dp[110][70][70];bool check(int x){if(x&(x<<1))return 0;//相邻1间隔是否合法if(x&(x<<2))return 0;//相邻2间隔是否合法return 1;
}void init(){//统计所有的可能合法状态,最多60种top=0;for(int i=0;i<(1<<m);i++){if(check(i))stk[top++]=i;}
}int count(int x){//统计x二进制中1的个数int cnt=0;while(x){if(x&1)cnt++;x=x>>1;}
//	while(x){//这个更快
//		cnt++;
//		x&=(x-1);
//	}	return cnt;
}int solve(){int ans=0;memset(dp,-1,sizeof(dp));for(int j=0;j<top;j++){//初始化第一行的状态num[j]=count(stk[j]);if(!(stk[j]&cur[1])){//和地图匹配dp[1][j][0]=num[j];//第一行状态为j,上一行状态为0(知道为啥从(1,1)开始初始化了把)ans=max(ans,dp[1][j][0]);}}for(int i=2;i<=n;i++){//处理每一行for(int j=0;j<top;j++){//遍历第i行的可能方案if(stk[j]&cur[i])continue;//是否和地图匹配for(int k=0;k<top;k++){//遍历第i-1行的可能方案if(stk[j]&stk[k])continue;//此行和上一行是否匹配(不用再判断和地图是否匹配,不匹配dp是-1,不影响的)for(int t=0;t<top;t++){//遍历上二行可能方案if(stk[j]&stk[t])continue;//此行和上二行是否匹配dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);}if(i==n)ans=max(ans,dp[i][j][k]);//不要放在外面套3个for取max}}}return ans;
}
int main(){while(cin>>n>>m){init();for(int i=1;i<=n;i++){scanf("%s",mp[i]+1);//加1是为了从1下标开始存}for(int i=1;i<=n;i++){cur[i]=0;for(int j=1;j<=m;j++){if(mp[i][j]=='H')//同样的,不能放的地方存1cur[i]+=(1<<(m-j));}}cout<<solve();}
}

这篇关于【算法每日一练]-动态规划(保姆级教程 篇13)POJ2686马车旅行 #POJ3254 玉米田 #POJ1185:炮兵阵地的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/490262

相关文章

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要

电脑没有仿宋GB2312字体怎么办? 仿宋GB2312字体下载安装及调出来的教程

《电脑没有仿宋GB2312字体怎么办?仿宋GB2312字体下载安装及调出来的教程》仿宋字体gb2312作为一种经典且常用的字体,广泛应用于各种场合,如何在计算机中调出仿宋字体gb2312?本文将为您... 仿宋_GB2312是公文标准字体之一,仿China编程宋是字体名称,GB2312是字php符编码标准名称(简

VScode连接远程Linux服务器环境配置图文教程

《VScode连接远程Linux服务器环境配置图文教程》:本文主要介绍如何安装和配置VSCode,包括安装步骤、环境配置(如汉化包、远程SSH连接)、语言包安装(如C/C++插件)等,文中给出了详... 目录一、安装vscode二、环境配置1.中文汉化包2.安装remote-ssh,用于远程连接2.1安装2

vscode保存代码时自动eslint格式化图文教程

《vscode保存代码时自动eslint格式化图文教程》:本文主要介绍vscode保存代码时自动eslint格式化的相关资料,包括打开设置文件并复制特定内容,文中通过代码介绍的非常详细,需要的朋友... 目录1、点击设置2、选择远程--->点击右上角打开设置3、会弹出settings.json文件,将以下内

SpringBoot实现动态插拔的AOP的完整案例

《SpringBoot实现动态插拔的AOP的完整案例》在现代软件开发中,面向切面编程(AOP)是一种非常重要的技术,能够有效实现日志记录、安全控制、性能监控等横切关注点的分离,在传统的AOP实现中,切... 目录引言一、AOP 概述1.1 什么是 AOP1.2 AOP 的典型应用场景1.3 为什么需要动态插

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

windos server2022的配置故障转移服务的图文教程

《windosserver2022的配置故障转移服务的图文教程》本文主要介绍了windosserver2022的配置故障转移服务的图文教程,以确保服务和应用程序的连续性和可用性,文中通过图文介绍的非... 目录准备环境:步骤故障转移群集是 Windows Server 2022 中提供的一种功能,用于在多个

龙蜥操作系统Anolis OS-23.x安装配置图解教程(保姆级)

《龙蜥操作系统AnolisOS-23.x安装配置图解教程(保姆级)》:本文主要介绍了安装和配置AnolisOS23.2系统,包括分区、软件选择、设置root密码、网络配置、主机名设置和禁用SELinux的步骤,详细内容请阅读本文,希望能对你有所帮助... ‌AnolisOS‌是由阿里云推出的开源操作系统,旨

PyTorch使用教程之Tensor包详解

《PyTorch使用教程之Tensor包详解》这篇文章介绍了PyTorch中的张量(Tensor)数据结构,包括张量的数据类型、初始化、常用操作、属性等,张量是PyTorch框架中的核心数据结构,支持... 目录1、张量Tensor2、数据类型3、初始化(构造张量)4、常用操作5、常用属性5.1 存储(st

Java操作PDF文件实现签订电子合同详细教程

《Java操作PDF文件实现签订电子合同详细教程》:本文主要介绍如何在PDF中加入电子签章与电子签名的过程,包括编写Word文件、生成PDF、为PDF格式做表单、为表单赋值、生成文档以及上传到OB... 目录前言:先看效果:1.编写word文件1.2然后生成PDF格式进行保存1.3我这里是将文件保存到本地后