codeforces 803E Roma and Poker (dp)

题目原文:http://codeforces.com/contest/803/problem/E

E. Roma and Poker

Each evening Roma plays online poker on his favourite website. The rules of poker on this website are a bit strange: there are always two players in a hand, there are no bets, and the winner takes 1 virtual bourle from the loser.

Last evening Roma started to play poker. He decided to spend no more thank virtual bourles — he will stop immediately if the number of his loses exceeds the number of his wins by k. Also Roma will leave the game if he wins enough money for the evening, i.e. if the number of wins exceeds the number of loses byk.

Next morning Roma found a piece of paper with a sequence on it representing his results. Roma doesn't remember the results exactly, and some characters in the sequence are written in a way such that it's impossible to recognize this character, so Roma can't recall whether he wonk bourles or he lost.

The sequence written by Roma is a strings consisting of charactersW (Roma won the corresponding hand), L (Roma lost),D (draw) and? (unknown result). Roma wants to restore any valid sequence by changing all ? characters to W,L orD. The sequence is calledvalid if all these conditions are met:

  • In the end the absolute difference between the number of wins and loses is equal tok;
  • There is no hand such that the absolute difference before this hand was equal tok.

Help Roma to restore any such sequence.

Input

The first line contains two numbersn (the length of Roma's sequence) and k (1 ≤ n, k ≤ 1000).

The second line contains the sequences consisting of charactersW,L,D and?. There are exactlyn characters in this sequence.

Output

If there is novalid sequence that can be obtained from s by replacing all? characters byW,L orD, printNO.

Otherwise print this sequence. If there are multiple answers, print any of them.


题目大意:给一个包含 'W' 'L' 'D' '?' 的序列,w表示胜利,l表示失败,d表示平局,?表示不确定,问你是否可以对?进行填充,得到一个合法的序列,序列要求在序列中间胜负差不得到 K,序列结束时胜负差必须为 K。


解题思路:

首先需要明确一个问题怎么判断能否形成一个满足要求的序列,因为题目要求比较严格,序列结束胜负差一定要为K。所以不是简单的贪心就可以解决的问题,要考虑搜索和dp。再看数据范围,应该是一道dp题目。接下来考虑怎么定义状态和状态转移。

简单思考可以得到如下结果:

状态:dp[i][j]表示前i步之后胜负差为 j(可正可负) 的情况是否存在。 

状态转移: 遍历dp[i-1][ -K ~ +K ] 然后根据之前累计的结果当前局的结果来维护结果

到这里我们已经有了一个基本正确的想法,现在再完善一些细节方面的问题。

①怎么保证序列中间不会有胜负差达到K的情况出现?

这个只需要除了最后一次每次转移的时候不要往dp[i][±K]转移就好。

②怎么记录一个下标可能为负的状态?

这个更简单了,把下标整体加上一个正数就好了,我是加了1000.

③最后怎么得到构造出来的序列?

正是因为这个问题所以我们的dp数组里面不能简单的维护0-1,需要保存他转移的信息,记录这个状态从何处来


AC代码:

/*
    @Author: wchhlbt
    @Date:   2017/4/28
*/
#include <bits/stdc++.h>

#define Fori(x) for(int i=0;i<x;i++)
#define Forj(x) for(int j=0;j<x;j++)
#define maxn 1007
#define inf 0x3f3f3f3f
#define ONES(x) __builtin_popcount(x)
using namespace std;

typedef long long ll ;
const double eps =1e-8;
const int mod = 1000000007;
typedef pair<int, int> P;
const double PI = acos(-1.0);
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};

char s[maxn];
int dp[maxn][2*maxn];

int main()
{
    //freopen("test.txt","r",stdin);
    int n,k;
    scanf("%d%d%s",&n,&k,s+1);
    int len = strlen(s+1);
    memset(dp,0,sizeof dp);
    if(s[1]=='W' || s[1]=='?')
        dp[1][1001] = 1000;
    if(s[1]=='D' || s[1]=='?')
        dp[1][1000] = 1000;
    if(s[1]=='L' || s[1]=='?')
        dp[1][999] = 1000;
    for(int i = 2; i<len; i++){
        if(s[i]=='W' || s[i]=='?')
            for(int j = 1001-k; j<=999+k; j++){
                if(dp[i-1][j-1])
                    dp[i][j] = j-1;
            }
        if(s[i]=='D' || s[i]=='?')
            for(int j = 1001-k; j<=999+k; j++){
                if(dp[i-1][j])
                    dp[i][j] = j;
            }
        if(s[i]=='L' || s[i]=='?')
            for(int j = 1001-k; j<=999+k; j++){
                if(dp[i-1][j+1])
                    dp[i][j] = j+1;
            }
    }
    if(s[len]=='W' || s[len]=='?'){
        if(dp[len-1][999+k])
            dp[len][1000+k] = 999 + k;
    }
    if(s[len]=='L' || s[len]=='?'){
        if(dp[len-1][1001-k])
            dp[len][1000-k] = 1001 - k;
    }

    if(dp[len][1000+k]==0 && dp[len][1000-k]==0)
        return 0*printf("NO\n");

    int now,last;
    if(dp[len][1000+k])
        now = 1000+k;
    else
        now = 1000-k;

    for(int i = len; i>=1; i--){
            last = now;
            now = dp[i][now];
            if(last-now==1)
                s[i]='W';
            else if(last-now==0)
                s[i]='D';
            else
                s[i]='L';
    }
    int cnt = 0;
    for(int i = 1; i<len; i++){//再判断以下序列是否合法
        if(s[i]=='W')   cnt++;
        else if(s[i]=='L')  cnt--;
        if(abs(cnt)==k)
            return 0*printf("NO\n");
    }
    printf("%s\n",s+1);
    return 0;
}

当然了,我的代码可能不够优雅,如果想写的更简洁些,可以看看这份代码~ 优雅代码


区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值