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;}
- HDU 女生赛
- HDU 6031(CCPC女生赛1009)
- HDU 2017女生赛04 (变形最短路)
- 2017CCPC女生赛 hdu 6030 Happy Necklace
- 2017CCPC女生赛 hdu 6029 Graph Theory
- 2017CCPC女生赛 hdu 6024 Building Shops
- 2017CCPC女生赛 hdu 6026 Deleting Edges
- 2017CCPC女生赛 hdu 6031 Innumerable Ancestors
- 女生赛HDU6023
- 女生赛HDU6024
- 女生赛hdu6025
- 女生赛hdu6027
- 2017 女生赛总结
- 2017 女生赛总结
- HDU-5703-Desert【2016CCPC女生专场】
- 2016 女生赛 1002 Desert
- 2016女生赛 1004 Clock【】
- hdu 2068 RPG 女生队成员 (错排 排列组合)
- 【博文搬家通知】
- 如何在linux下安装mysql数据库并配置
- bash文本输入
- 数字图像处理的插值方法
- android stdio 中查看Log日志定位问题
- HDU 女生赛
- CString 操作指南
- opencv 2.4.3 配置VS2010
- create操作符
- PHP 操作 Excel PHPExcel 详解
- 用Ant实现Java项目的自动构建和部署
- NYOJ 1274排兵布阵
- 两个类互相包含的问题
- this