动态规划练习第二弹

1.照明系统设计——UVa 11400

 题目链接:https://vjudge.net/contest/232313#problem/F

 思路:既然只能从电压低到电压高替换,那么就相对于电压排序,注意每种电压的灯泡要么全换要么全不换,不然会耗费两种电源的钱。

排序后,设s[i]为前i种灯泡的数量和,d[i]为灯泡1~i的最小花费,则有

                                            d[i]=min{d[j]+(s[i]-s[j])*c[i]+k[i]}

表示前j个用最小开销,第j+1~i个用第i号的电源的最小花销,所以遍历i从1~n,j从0~i,表示遍历前j个开销的最小值。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int INF=0x3f3f3f3f;

const int maxn=1005;
struct Node
{
    int v,k,c,l;
    Node(int _v=0,int _k=0,int _c=0,int _l=0):v(_v),k(_k),c(_c),l(_l){}
    bool operator <(const Node &rhs)
    {
        return v<rhs.v;
    }
}node[maxn];


int s[maxn];
int dp[maxn];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)break;
        s[0]=0;
        for(int i=1;i<=n;i++)
        {
            int v,k,c,l;
            scanf("%d%d%d%d",&v,&k,&c,&l);
            node[i]=Node(v,k,c,l);
        }
        sort(node+1,node+n+1);
        for(int i=1;i<=n;i++)
        {
            s[i]=s[i-1]+node[i].l;
        }
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            dp[i]=INF;
            for(int j=0;j<i;j++)
            {
                dp[i]=min(dp[i],dp[j]+(s[i]-s[j])*node[i].c+node[i].k);
            }
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}

2.划分为回文串

题目链接:https://vjudge.net/contest/232313#problem/G

思路:用d[i]表示从字符0~i个中可以划分为回文串的最小个数,则d[i]=min{d[j]+1|s[j+1~i]是回文串}。这个转移还是很清晰的,所以需要预处理s[i....j]是否为回文串,时间复杂度为O(n^2),状态O(n)个,决策O(n)个,时间复杂度为O(n^2)。

(其实好像是常数太大了吧,O(n^3)更快一些。)

代码一:O(n^3)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int INF=0x3f3f3f3f;

const int maxn=1010;
char c[maxn];
int dp[maxn];

bool ishuiwen(int l,int r)
{
    while(l<r)
    {
        if(c[l]!=c[r])return false;
        l++;
        r--;
    }
    return true;
}


int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",c+1);
        int len=strlen(c+1);
        dp[0]=0;
        for(int i=1;i<=len;i++)
        {
            dp[i]=i+1;
            for(int j=1;j<=i;j++)
            {
                if(ishuiwen(j,i))
                {
                    dp[i]=min(dp[i],dp[j-1]+1);
                }
            }
        }
        printf("%d\n",dp[len]);
    }
    return 0;
}

代码二:O(n^3)

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <string>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
char s[1010];
int f[1010][1010], dp[1010];
int x, y, len;
void DP()
{
    int flag = 1;
    for (int i = x, j = y; i >= 1 && j <= len; i--, j++){
        if (flag){
            if (s[i] == s[j]) f[i][j] = 1;
            else f[i][j] = INF, flag = 0;
        }
        else f[i][j] = INF;
    }
}

int main()
{
    dp[1] = 1;
    int t;
    scanf("%d", &t);
    while (t--){
        scanf("%s", s+1);
        len = strlen(s+1);
        f[len][len] = 1;
        for (x = 1; x < len; x++)
            for (y = x; y <= x+1; y++) DP();
        for (int i = 2; i <= len; i++){
            dp[i] = INF;
            for (int j = 0; j < i; j++) dp[i] = min(dp[i], dp[j]+f[j+1][i]);//判断的是从j+1到i是不是回文
        }
        printf("%d\n", dp[len]);
    }
    return 0;
}

3.牛客小白月赛7——B.自杀游戏

题目链接:https://www.nowcoder.com/acm/contest/190/B

思路:组合游戏DP,注意两点状态的变换:

           1.必败态之后就是必胜态

           2.因为每个人都可以将时间加快[a,b]的任意时间,那么如果在t-(a+1)~t-(b+1)的时间范围内,如果有必败态,即dp[j]=0,都可以通过通过调快时间使dp[i]为必胜态,理解了这一点这个题目就好做了。具体细节看代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

const int maxn=1e5+10;
int dp[maxn];
int main()
{
    int a,b,n;
    while(scanf("%d%d%d",&n,&a,&b)!=EOF)
    {
        //memset(dp,0,sizeof(dp));
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            if(dp[i-1]==0)
            {
                dp[i]=1;//必败态之后为必胜态
            }
            else
            {
                if(i<a+1)//不能调,必胜态之后为必败态
                    dp[i]=0;
                for(int j=a+1;j<=b+1;j++)//可调时间范围
                {
                    if(i>=j&&dp[i-j]==0)//如果在t-(a+1)~t-(b+1)的时间段内有必败态,都可以通过调整时间成为必胜态。
                    {
                        dp[i]=1;
                        break;
                    }
                }
                if(dp[i]!=1)dp[i]=0;
            }
        }
        if(dp[n]==1)
        {
            puts("Alice");
        }
        else
        {
            puts("Bob");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Q755100802/article/details/82917920