HDU 女生赛

来源:互联网 发布:餐饮取名软件 编辑:程序博客网 时间:2024/05/06 09:23

1006 (DP)

题意:给出两个字符串能否在不改变相对顺序的情况下组成另一个字符串。比如:

ab,cd->acbd。

考虑dp,dp[i][j]表示C串的前i+j个字符能不能用a,b的某段前缀拼成,那么转移就

很简单了类似于求LCS的dp:dp[i][j]=1(dp[i-1][j]==1 && a[i]==c[i+j]);dp[i][j]=1

(dp[i][j-1]==1 && b[j]==c[i+j]).

trick:注意特判len(a)+len(b)!=len(c);贪心是不对的能够找出反例的。

#include <cstdio>#include <cmath>#include <algorithm>#include <iostream>#include <cstring>#include <vector>#include <map>#include <queue>using namespace std;#define maxn 2111bool dp[maxn][maxn];char a[maxn], b[maxn], c[maxn];int main () {    while (scanf ("%s%s%s", a+1, b+1, c+1) == 3) {        int n = strlen (a+1), m = strlen (b+1), len = strlen (c+1);        if (n+m != len) {            printf ("No\n");            continue;        }        memset (dp, 0, sizeof dp);        a[0] = b[0] = c[0] = 1;        dp[0][0] = 1;        for (int i = 0; i <= n; i++) {            for (int j = 0; j <= m; j++) {                if (i && a[i] == c[i+j] && dp[i-1][j])                     dp[i][j] = 1;                if (j && b[j] == c[i+j] && dp[i][j-1])                    dp[i][j] = 1;            }        }        printf ("%s\n", dp[n][m] ? "Yes" : "No");    }    return 0;}

1007 (博弈)

题意:在n*m的棋盘上,初始点在[n][m],A,B两人轮流操作,可以把点移动到

[x+k][y+k]或者[x+1][y]或者[x][y+1],谁先无法移动则失败(移动时点在[1][1])。

输出必胜的人。

(蹲坑的时候发现的水题居然过的人这么少23333)打个表很容易发现规律,开一个

布尔dp数组dp[i][j]表示点在(i,j)先手必胜(1)还是必败(0)。然后很容易转移了,一个

状态是必胜态当且仅当能够走向必败态,否则如果只能走向必胜态就是必败态。

打表代码也很容易~

#include <iostream>using namespace std;#define maxn 111bool dp[maxn][maxn];int k;int main () {    cin >> k;    dp[1][1] = 0;//必败     for (int i = 1; i <= 20; i++) {        for (int j = 1; j <= 20; j++) {            if (i == 1 && j == 1) continue;            bool find = 0;//后继状态中有没有发现必败态            if (i-1 && !dp[i-1][j]) {                find = 1;            }            if (j-1 && !dp[i][j-1]) {                find = 1;            }            if (i-k > 0 && j-k > 0 && !dp[i-k][j-k]) {                find = 1;            }            if (find)                dp[i][j] = 1;            else                 dp[i][j] = 0;        }    }    for (int i = 1; i <= 20; i++) {        for (int j = 1; j <= 20; j++) {            cout << dp[i][j] << " ";        } cout << endl;    }    return 0;}

我们发现k取不同值时状态图是有规律的:

k=1:


k=2:


k=3:


这个规律很显然了:


注意k=1的时候规律有所不同,需要特判一下。

#include <cstdio>#include <cmath>#include <algorithm>#include <iostream>#include <cstring>#include <vector>#include <map>#include <queue>using namespace std;#define maxn 111int q, k, m, n;bool solve () {    if (n > m) swap (m, n);    if (n%(k+1) == 0)         return 1;    if (k == 1) {        int num1 = n/2;        n -= num1, m -= num1;        if ((n+m)&1)            return 1;        return 0;    }    int num1 = n/(k+1);    int op = num1&1;    n -= num1, m -= num1;    if ((n+m)&1) {        return op^1;    }    else         return op;    return 0;}int main () {    int t;    scanf ("%d", &t);    while (t--) {        scanf ("%d%d", &q, &k);        while (q--) {            scanf ("%d%d", &n, &m);            printf ("%s\n", solve () ? "Alice" : "Bob");        }    }    return 0;}


1008 不会=。=

1009 (数学 贪心)

题意:定义S(n)表示n每一位的和,求最小的n使得a*S(n)=b*S(2n).

考虑S(n)和S(2n)之间的关系。因为某一位*2导致的进位不会影响前一位的进位,所以

可以每一位分开讨论。还有一个很显然的就是答案不可能包含0数位。

对于某一位如果是[1,4],那么这位*2相当于n*2这一位的数字;如果是[5,9]那么这位*2

相当于n*2这位(如果有进位还要算上进位的数字)的数字-9。所以这和[5,9]区间内的

数字个数相关。假设有p个,那么原始转化为:

a*sn = b*(sn*2-p*9),进而sn=p*(9b/(2b-a)).所以对于确定的p,我们就知道了n的数位

和。而n至少p位,为了使n最小先填上p个5,然后多余的补9(根据贪心,数位越短

越好),在多余的前面补4(注意不能再补[5,9]区间的数字了,长度p已经被限制了)。

最后剩下的数放在最前面。

trick:无解的特判很关键,2b-a不能为负,p位5的和不能超过S(n)。

#include <cstdio>#include <cstring>#include <cstdlib>#include <algorithm>#include <cmath>#include <iostream>using namespace std;#define maxn 11111int ans[maxn];int a, b;void solve (int x, int y) {    int g = __gcd (x, y);    x /= g, y /= g;    int cnt = 0;    for (; cnt < y; cnt++) {        ans[cnt] = 5;        x -= 5;    }    for (int i = 0; i < y && x; i++) {        if (x >= 4) {            ans[i] += 4;            x -= 4;        }        else {            ans[i] += x;            x = 0;        }    }    for (; x >= 4; x -= 4, cnt++) {        ans[cnt] = 4;    }    if (x) ans[cnt++] = x;    for (int i = cnt-1; i >= 0; i--) {        printf ("%d", ans[i]);    }    printf ("\n");}int main () {    int t;    scanf ("%d", &t);    while (t--) {        scanf ("%d%d", &a, &b);        int x = 9*b, y = 2*b-a;        if (y == 0) {            cout << "1" << "\n";            continue;        }        if (y < 0) {            cout << "0" << "\n";            continue;        }        if (y*5 > x) {            cout << "0" << "\n";            continue;        }        solve (x, y);    }    return 0;}

1010 (状压DP 优先队列)

题意:给出n个点,每个点hack会有分数,分数为a[i] ,每hack一次减少b[i]最少到0。

然后最长走l的距离,最多hack k次,求从0出发回到0的最大hack分数。

显然我们hack某一个点肯定是到达以后就hack这么多次最划算。那么只需要对于

某种可达点集求一下最大的分数。所以题目分成了两部分:求可达点集,求最大分数。

求最大分数很简单,根据贪心原理,我们肯定是选取最大的分数,所以我们先把可达

点集中的分数都扔进优先队列,每次取最大的分数,然后减去相应的b[i]再扔进

优先队列,注意控制次数不能超过k。

求可达点集。因为n范围很小,考虑状压。dp[state][i]表示当前走过的点集二进制状态

为state并且现在在点i的走过的最短路径,转移就是dp[state][i]=min (dp[state][i],

dp[state`][j]+dis[i][j]),这里任意两点的距离经常需要用到所以我们用floyd先跑一下

两两的最短路。需要注意这个和一般的TSP不同(每个点可经过多次),所以枚举点

的时候可以枚举走过的点。

trick:maxn开17的时候莫名re(虽然我觉得足够了233)

#include <cstdio>#include <cmath>#include <algorithm>#include <iostream>#include <cstring>#include <vector>#include <map>#include <queue>using namespace std;#define maxn 18#define INF 1000000000int n, m, k, l;int dis[maxn][maxn];int a[maxn], b[maxn];void floyd () {    for (int i = 0; i <= n; i++) {        for (int u = 0; u <= n; u++) {            for (int v = 0; v <= n; v++) {                dis[u][v] = min (dis[u][v], dis[u][i] + dis[i][v]);            }        }    }}int dp[1<<maxn][maxn];struct node {    int id, num;    bool operator < (const node &a) const {        return num < a.num;    }};priority_queue <node> q;int aa[maxn];int cal (int state) {    for (int i = 1; i <= n; i++) aa[i] = a[i];    int ans = 0;    while (!q.empty ())        q.pop ();    int cnt = k;     for (int i = 1; i <= n; i++) if (state&(1<<i)) {        q.push ((node) {i, aa[i]});    }    while (cnt && !q.empty ()) {        node now = q.top (); q.pop ();         cnt--;        ans += now.num;        aa[now.id] -= b[now.id];        if (aa[now.id] > 0) {            q.push ((node) {now.id, aa[now.id]});        }    }    return ans;}int solve () {    int Max = (1<<(n+1));    int ans = 0;    for (int i = 0; i < Max; i++) {        for (int j = 0; j <= n; j++) dp[i][j] = INF;    }    dp[1][0] = 0;    for (int i = 1; i < Max; i++) {//枚举当前状态        for (int pre = 0; pre <= n; pre++) if (i&(1<<pre)) {//枚举前一终点            for (int now = 0; now <= n; now++) {//枚举当前终点                dp[i+(1<<now)][now] = min (dp[i+(1<<now)][now], dp[i][pre] + dis[pre][now]);            }        }    }    for (int i = 1; i < Max; i++) { //cout << i << endl;        int Min = INF;        for (int j = 1; j <= n; j++) if (i&(1<<j)) {            Min = min (Min, dp[i][j]+dis[j][0]);        }         if (Min <= l) {             ans = max (ans, cal (i));        }    }    return ans;}int main () {    int t, kase = 0;    scanf ("%d", &t);    while (t--) {        scanf ("%d%d%d%d", &n, &m, &k, &l);        for (int i = 0; i <= n; i++) {            for (int j = 0; j <= n; j++) {                if (i == j) dis[i][j] = 0;                else dis[i][j] = INF;            }        }        for (int i = 1; i <= n; i++) scanf ("%d", &a[i]);        for (int i = 1; i <= n; i++) scanf ("%d", &b[i]);        for (int i = 1; i <= m; i++) {            int u, v, c;            scanf ("%d%d%d", &u, &v, &c);            dis[u][v] = min (dis[u][v], c);            dis[v][u] = c;        }        floyd ();        printf ("Case %d: %d\n", ++kase, solve ());    }    return 0;}


0 0
原创粉丝点击