A. King’s Cake(HDU5640)
思路
不妨假设 n>m ,那么国王会一直切下 m×m 大小的蛋糕,直到 n≤m 为止,然后重复切下 n×n 大小的蛋糕直到 n≥m ……最后当 n=m 时国王就得到了最后一个蛋糕。实现这个过程只需模拟这个过程让两个数反复相减即可。另外,用辗转相除法可以加速这个过程。
代码
#include <cstdio>
int t, n, m;
int gcd(int a, int b) {
return b ? a / b + gcd(b, a % b) : 0;
}
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
printf("%d\n", gcd(n, m));
}
return 0;
}
B.King’s Phone(HDU5641)
思路
已知满足要求的密码有三个条件:
- 密码的长度至少是4
- 每个数字只能出现一次
- 经过密码的两个数字时不能路过没访问过的数字
那么针对这三个条件相应地进行三种判断:
- 直接判断密码长度是否满足条件
- 用vis[]数组记录该密码是否已经出现
- 预处理m[][]数组,m[i][j] = k表示键盘上i,j数字之间隔着k,那么可以遍历密码,若储存密码的数组为a,则m[a[i]][a[i-1]]无对应的k,或k已经出现过则满足条件
代码
#include <cstdio>
#include <cstring>
bool vis[10];
int t, k, a[10], m[10][10];
void init() {
memset(m, 0, sizeof(m));
for(int i = 1; i < 10; i++) {
for(int j = 1; j < 10; j++) {
if(i + j == 10) {
m[i][j] = 5;
}
}
}
m[1][3] = m[3][1] = 2; m[7][9] = m[9][7] = 8;
m[1][7] = m[7][1] = 4; m[3][9] = m[9][3] = 6;
}
bool judge() {
if(k < 4) return false;
memset(vis, 0, sizeof(vis));
for(int i = 0; i < k; i++) {
if(a[i] < 1 || a[i] > 9 || vis[a[i]]) {
return false;
}
vis[a[i]] = true;
if(i == 0) continue;
int mid = m[a[i]][a[i-1]];
if(mid && vis[mid] == false) {
return false;
}
}
return true;
}
int main() {
init();
scanf("%d", &t);
while(t--) {
scanf("%d", &k);
for(int i = 0; i < k; i++) {
scanf("%d", &a[i]);
}
puts(judge() ? "valid" : "invalid");
}
}
C.King’s Order(HDU5642)
思路
读题的时候就感觉这题可以利用最优子结构,因此应该是个动态规划的题目。然后觉得应该是通过计算不符合条件的情况来间接得到结果。但是这种思路似乎是不太好找到转移方程的。于是还是用直接的方法来解决。假设d[i]表示长度为i的合法字符串的数量,那么考虑是否可以利用
d[i−1]
来得到
d[i]
。对于长度为
i
的字符串的
代码
#include <cstdio>
const int maxn = 2e3 + 5, mod = 1e9 + 7;
int t, n;
long long d[maxn][4];
int main() {
d[1][1] = 26;
for(int i = 2; i < maxn; i++) {
d[i][1] = 25 * (d[i-1][1] + d[i-1][2] + d[i-1][3]) % mod;
d[i][2] = d[i-1][1];
d[i][3] = d[i-1][2];
}
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
printf("%I64d\n", (d[n][1] + d[n][2] + d[n][3]) % mod);
}
return 0;
}
思路
后来才知道,一维的动态规划已经足以表示解题需要的所有信息。我们可以将状态转移方程写成这样
代码
#include <cstdio>
const int maxn = 2e3 + 5, mod = 1e9 + 7;
int t, n;
long long d[maxn][4];
int main() {
d[1][1] = 26;
for(int i = 2; i < maxn; i++) {
d[i][1] = 25 * (d[i-1][1] + d[i-1][2] + d[i-1][3]) % mod;
d[i][2] = d[i-1][1];
d[i][3] = d[i-1][2];
}
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
printf("%I64d\n", (d[n][1] + d[n][2] + d[n][3]) % mod);
}
return 0;
}
D.King’s Game(HDU5643)
思路
这是约瑟夫环问题的变形。如果用链表来做模拟的话,每组的复杂度都将是
代码
#include <cstdio>
int t, n, f;
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
f = 0;
for(int i = 2; i <= n; i++) {
f = (f + n - i + 1) % i;
}
printf("%d\n", f + 1);
}
return 0;
}
E.King’s Pilots(HDU5644)
思路
读题以后发现这是个最优化问题。并且与解相关的量太多,似乎用贪心,搜索和动态规划都不好办。但发现题目中的条件有点“供需关系”的意思。于是考虑是否能用网络流(这里应该是费用流)来解决。
先设置
这样,图就建好了。对这个图运行一次最小费用最大流。当最大流等于
∑i=1np[i]
的时候最小费用就是解。否则无解。
代码
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 405, maxm = 10, INF = 1e9;
int cs, n, k, m, P, Q, S, T, sum, ans, p[maxn], s[maxm], t[maxm];
// 存储边的信息的结构体
struct edge {
int from, to, cap, flow, cost;
edge(int from, int to, int cap, int flow, int cost):
from(from), to(to), cap(cap), flow(flow), cost(cost) {}
};
// 最小费用最大流模板
struct MCMF {
int v;
vector <edge> edges;
vector <int> G[maxn];
int inq[maxn]; // 是否在队列中
int d[maxn]; // Bellman-Ford
int p[maxn]; // 上一条弧
int a[maxn]; // 可改进量
void init(int v) {
this->v = v;
for(int i = 0; i < v; i++) {
G[i].clear();
}
edges.clear();
}
// 往邻接表中添加一条边
void addEdge(int from, int to, int cap, int cost) {
G[from].push_back(edges.size());
edges.push_back(edge(from, to, cap, 0, cost));
G[to].push_back(edges.size());
edges.push_back(edge(to, from, 0, 0, -cost));
}
// 用Bellman-Ford算法寻找最短增广路
bool BellmanFord(int s, int t, int& flow, int& cost) {
for(int i = 0; i < v; i++) {
d[i] = INF;
}
memset(inq, 0, sizeof(inq));
d[s] = 0;
inq[s] = 1;
p[s] = 0;
a[s] = INF;
queue <int> q;
q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
inq[u] = 0;
for(int i = 0; i < G[u].size(); i++) {
edge& e = edges[G[u][i]];
if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
d[e.to] = d[u] + e.cost;
p[e.to] = G[u][i];
a[e.to] = min(a[u], e.cap - e.flow);
if(!inq[e.to]) {
q.push(e.to);
inq[e.to] = 1;
}
}
}
}
if(d[t] == INF) return false; // s-t不连通,失败退出
flow += a[t];
cost += d[t] * a[t];
for(int u = t; u != s; u = edges[p[u]].from) {
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
return true;
}
// 解出最小费用和最大流
int Mincost(int s, int t) {
int flow = 0, cost = 0;
while(BellmanFord(s, t, flow, cost));
if(flow == sum) return cost;
else return -1;
}
}o;
int main() {
scanf("%d", &cs);
while(cs--) {
scanf("%d%d", &n, &k);
sum = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &p[i]);
sum += p[i];
}
scanf("%d%d%d", &m, &P, &Q);
for(int i = 1; i <= m; i++) {
scanf("%d%d", &s[i], &t[i]);
}
S = 0, T = 2 * n + 1;
// 初始化邻接表
o.init(2 * n + 2);
// 供应初始的免费飞行员
o.addEdge(S, n + 1, k, 0);
// 供应训练好的飞行员
if(P <= n) {
o.addEdge(S, n + P, INF, Q);
}
// 存储休假回来的飞行员
for(int i = 1; i <= n; i++) {
o.addEdge(S, i, p[i], 0);
}
// 供应休假回来的飞行员
for(int i = 1; i <= m; i++) {
for(int j = 1; j + t[i] <= n; j++) {
o.addEdge(j, n + j + t[i], INF, s[i]);
}
}
// 使用前几天未使用的飞行员
for(int i = 1; i < n; i++) {
o.addEdge(n + i, n + i + 1, INF, 0);
}
// 飞行员去休假
for(int i = 1; i <= n; i++) {
o.addEdge(n + i, T, p[i], 0);
}
// 判断是否有解并输出
ans = o.Mincost(S, T);
if(ans < 0) {
puts("No solution");
}
else printf("%d\n", ans);
}
return 0;
}