NYOJ-81 炮兵阵地

题目:转送门


这题用动态规划做,这动态规划可真够复杂的。

总结我网上看到的资料:

1,由于列数较少,所以以行为动态规划过程的一个状态。

2,每行的炮兵状态也可以统计出来,用status[i]表示第i个状态,status_[i]表示第i个状态的炮兵部队数。

3,状态转移方程:dp[r][i][j]=max{dp[r-1][j][k]+status_num[i]};

dp[r][i][j]表示第r行状态为i,第r-1行状态为j的时候最大炮兵部队数。遍历k状态,并保证i,j,k不冲突。

为什么要dp[r][i][j]而不是dp[r][i]呢?

若用dp[r][i],那状态转移方程为:dp[r][i]=max{dp[r-1][j]+status_num[i]},可是这样子的话,就不能保证第r行与第r-2行的状态不冲突了。


001. #include <iostream>
002. #include <string.h>
003. using namespace std;
004.  
005. #define max(a,b) ((a)>(b)?(a):(b))
006. int dp[102][65][65];//dp值,记录某一行相应状态下的最大炮兵数
007. int status[65];//一行中,所有不冲突炮兵部队状态数
008. int status_num[65];//对应于一种炮兵部队状态的炮兵部队数
009. int map[102];//对地形的状态压缩
010.  
011. int main()
012. {
013. int t,row,col;
014. char tmp;
015. cin>>t;
016. while(t--)
017. {
018. memset(map,0,sizeof(map));
019. memset(dp,0,sizeof(dp));
020. memset(status,0,sizeof(status));
021. memset(status_num,0,sizeof(status_num));
022. cin>>row>>col;
023. //输入地形,用1表示高山,0表示平原
024. for(int i=1;i<=row;i++)
025. {
026. for(int j=0;j<col;j++)
027. {  
028. cin>>tmp;
029. if(tmp=='H')
030. map[i]|=1<<j;
031. }
032. }
033. if(row==0)
034. {
035. cout<<'0'<<endl;
036. continue;
037. }
038. //统计一行所有可能的满足条件的炮兵部队状态及相应的炮兵部队数
039. int sum=0;
040. for(int i=0;i<(1<<col);i++)
041. {
042. if(i&(i<<1)||i&(i<<2))
043. continue;
044. int count=0,x=i;
045. while(x)
046. {
047. count++;
048. x&=(x-1);
049. }
050. status[sum]=i;
051. status_num[sum++]=count;
052. }
053. //状态转移方程:dp[r][i][j]=max(dp[r-1][j][k]+status[i])
054. //dp[r][i][j], 表示第r行的状态为i,第r-1状态为j,这种情况下的最大炮兵数
055. //处理第1行
056. for(int i=0;i<sum;i++)
057. {
058. if(status[i]&map[1])//与地形有冲突
059. continue;
060. dp[1][i][0]=status_num[i];
061. }
062. //处理第2行
063. for(int i=0;i<sum;i++)
064. {
065. if(status[i]&map[2])//与地形有冲突
066. continue;
067. for(int j=0;j<sum;j++)//检验是否与第1行有冲突
068. {
069. if(status[j]&map[1])//寻找第1行不与地形冲突的状态
070. continue;
071. if(status[i]&status[j])//判断第2行是否与第1行冲突
072. continue;
073. dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+status_num[i]);
074. }
075. }
076. //处理之后的行
077. for(int r=3;r<=row;r++)
078. {
079. for(int i=0;i<sum;i++)//寻找第r行的状态
080. {
081. if(status[i]&map[r])//检验是否与地形冲突
082. continue;
083. for(int j=0;j<sum;j++)//寻找第r-1行的状态
084. {
085. if(status[j]&map[r-1])//检验是否与地形冲突
086. continue;
087. if(status[i]&status[j])//检验第r-1是否与第r冲突
088. continue;
089. for(int k=0;k<sum;k++)
090. {
091. if(status[k]&map[r-2])//检验是否与地形冲突
092. continue;
093. if((status[i]&status[k])||(status[j]&status[k]))//检验第r-2是否与第r、r-1冲突
094. continue;
095. dp[r][i][j]=max(dp[r][i][j],dp[r-1][j][k]+status_num[i]);
096. }
097. }
098. }
099. }
100.  
101. int max_num=0;
102. for(int i=0;i<sum;i++)
103. for(int j=0;j<sum;j++)
104. if(dp[row][i][j]>max_num)
105. max_num=dp[row][i][j];
106. cout<<max_num<<endl;
107. }
108. return 0;
109. }

参考:http://blog.csdn.net/hearthougan/article/details/21374473



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值