URAL 1225 Flags 简单DP,一重循环

题意:

窗口可放n面红蓝白三种旗,规定同色不相邻,蓝在红白之间。共有多少种放置方法。

 

思路:

设dp[i][j]表示有i面旗,第i面旗填颜色j(j=01表示红白)时的总数,第i面填j色时,i-1可以填1-j(红白相间)或者蓝色,两种填法的计算:

(1)填1-j时有dp[i-1][1-j]种   

(2)填蓝色时i-2和i要填红白色才能将i-1的蓝色包围,即i-2要填1-j,共dp[i-2][1-j]种

所以,状态转移方程为dp[i][j]=dp[i-1][1-j]+dp[i-2][1-j]。答案是dp[n][1]+dp[n][0]。

 

空间优化:

每一轮循环都要计算

dp[i][0] = dp[i-1][1]+dp[i-2][1]

dp[i][1] = dp[i-1][0]+dp[i-2][0]

两式相加得:(dp[i][0] + dp[i][1]) = (dp[i-1][0]+ dp[i-1][1]) + (dp[i-2][0]+dp[i-2][1])

两两结构相同,去掉第二维,dp[i] 表示前i面旗的放置方法总数,第i面旗可以填红白色。

由上面的方程得:dp[i] = dp[i-1] + dp[i-2]

 

直接给dp[i]分类很快,就是凑题解字数时,发现这种降维的方法,和背包的降维不一样。

状态转移分析:

第i面红色,i-1面白色:dp[i-1]是i-1填红白色的总数,红白各一半,白色有dp[i-1]/2种

第i面红色,i-1面蓝色:i-2必须是白色,则有dp[i-2]/2种

第i面白色,i-1面红色:同第一种,有dp[i-1]/2种

第i面白色,i-1面蓝色:同第二种,有dp[i-2]/2种

四个加起来得到dp[i]。

 

初始化:

dp[i]依赖于前两个,所以至少要提供两个连续的dp[i],可以是dp[0] dp[1]或dp[1] dp[2]

 

算法步骤:

步骤1:初始化为dp[i]=0,dp[1]=2

步骤2:递推求dp[i],答案是dp[n]

 

算法复杂度:

初始化和递推都是一个循环,时间复杂度O(n),空间复杂度O(n)

 

代码:

#include <cstdio>
const int MAX_N = 46;
long long int cnt[MAX_N];
int main() 
{ 
    int n;
    scanf("%d", &n);
    cnt[1] = cnt[2] = 2;
    for (int i = 3; i <= n; ++i)
        cnt[i] = cnt[i - 1] + cnt[i - 2];
    printf("%lld\n", cnt[n]);
    return 0;
}
   

 

突然想到前面那个完全背包的输入,第i个物品用完就不用了,不用用数组保存。

这道题只要维护三个值dp[i] dp[i-1] dp[i-2]就可以了,也就是

c = a + b; 
b = a; 
a = c;  -> a+b未更新

就是

a = a + b; 
b = a – b; // (a旧值+b)-b

 

维护两个变量就行了OoO ,循环用while连for里面的局部变量也省了,代码好短

算法步骤见代码,main函数四行,一行输入,一行计算,一行输出,一行return 0

代码:

#include <cstdio> 
int n;
long long a=2,b=0;  // a=[1] b=a[0] 
int main() 
{ 
    scanf("%d", &n);  
    while(--n)  a = a + b , b = a - b;  
    printf("%lld\n", a); 
    return 0;
}

 

  

 

你可能感兴趣的:(URAL 1225 Flags 简单DP,一重循环)