codeforces 803E Roma and Poker (dp)

来源:互联网 发布:硬盘使用寿命 知乎 编辑:程序博客网 时间:2024/05/14 05:55

题目原文: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 byk. 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;}
当然了,我的代码可能不够优雅,如果想写的更简洁些,可以看看这份代码~ 优雅代码


0 0
原创粉丝点击