HDU-1559最大子矩阵
这是一道很裸的二维前缀和题,如果没了解过前缀和,暴力枚举每种情况,时间复杂度是O(N^4),N为1000,也就是10^12次方,而计算机一秒大概是10^8或10^9,显然会TLE。
预处理出前缀和之后,遍历每个子矩阵的时间O(N^2)就优化成了O(1)
样例输入:
1
4 5 2 2
3 361 649 676 588
992 762 156 993 169
662 34 638 89 543
525 165 254 809 280
样例输出:
2474
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int arr[N][N];
int s[N][N];
int n, m, x, y;
int main()
{
int t;
cin >> t;
while (t--) {
cin >> n >> m >> x >> y;
memset(arr, 0, sizeof(arr));
memset(s, 0, sizeof(s));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> arr[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + arr[i][j];
}
}
int ma = 0;
for (int i = x; i <= n; i++) {
for (int j = y; j <= m; j++) {
ma = max(ma, s[i][j] - s[i - x][j] - s[i][j - y] + s[i - x][j - y]);
}
}
cout << ma << endl;
}
return 0;
}
(1)当使用前缀和算法时,如果下标从0开始,因为要用到i - 1这种下标,必须要进行判断,否则会数组越界。所以下标从1开始,减少一些边界判断。
(2)多组输入数据记得将使用过的数组初始化,否则会WA。
POJ-3984 迷宫问题
样例输入:
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
样例输出:
(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)
这是很经典的bfs模板题,相对于直接求最短路来说,输出方案《有点》麻烦。(输出方案的几行代码debug花了1个小时 😥 😥 😥 😥 😥 )
#include<iostream>
#include<queue>
using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second
int dx[] = { 0,-1,0,1 };
int dy[] = { -1,0,1,0 };
int g[10][10];
queue<PII>q;
PII pre[10][10];
int st[10][10];
int dis[10][10];
void bfs(int sx, int sy)
{
q.push(make_pair(sx, sy));
st[sx][sy] = 1;
while (q.size()) {
PII t = q.front();
q.pop();
for (int i = 0; i < 4; i++) {
int a = dx[i] + t.x;
int b = dy[i] + t.y;
if (st[a][b])continue;
if (a < 0 || a >= 5 || b < 0 || b >= 5)continue;
if (g[a][b]) continue;
st[a][b] = 1;
q.push(make_pair(a, b));
dis[a][b] = dis[t.x][t.y] + 1;
pre[a][b] = make_pair(t.x, t.y);
if (a == 0 && b == 0) {
return;
}
}
}
}
int main()
{
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
cin >> g[i][j];
}
}
bfs(4, 4);
//cout << dis[0][0] << endl;
int x1 = 0, y1 = 0;
cout << "(0, 0)" << endl;
int a, b;
while (x1 != 4 && y1 != 4) {
cout << "(" << pre[x1][y1].x << ", " << pre[x1][y1].y << ")" << endl;
a = pre[x1][y1].x;
b = pre[x1][y1].y;
x1 = a;
y1 = b;
}
cout << "(" << pre[x1][y1].x << ", " << pre[x1][y1].y << ")" << endl;
cout << "(4, 4)" << endl;
/*cout << pre[2][0].x<<" "<<pre[2][0].y << endl;
cout << pre[2][4].x << " " << pre[2][4].y << endl;*/
return 0;
}
当时51,52行代码 我写的是 x1=pre[x1][y1].x y1=pre[x1][y1].y 差点人都de没了,一直没想到是这出了错误。
原因:当pre[x1][y1].x赋值给x1后,后面想赋值给y1的时候,其实赋的是pre[pre[x1][y1]][y1].y
所以需要要用俩个变量存下来,然后再赋值给x1,y1;
BFS算法的时间复杂度是O(N),如何确保每个点只遍历1次呢?
大部分的bfs需要用到判重数组st,如果当前这个点遍历到了,就将其标记。
UESTC-32 树上战争
样例输入:
2 1
1 2
1 2
5 2
1 2
1 3
3 4
3 5
4 2
4 5
0 0
样例输出:
lxh
pfz
lxh
题意:谁最靠近公共祖先谁就赢。
算法:并查集
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int p[N];
int main()
{
while (cin >> n >> m) {
if (!n && !m) {
break;
}
for (int i = 1; i <= n; i++) {
p[i] = i;
}
for (int i = 0; i < n - 1; i++) {
int a, b;
cin >> a >> b;
p[b] = a;
}
while (m--) {
int a, b;
cin >> a >> b;
int ans1 = 0;
int ans2 = 0;
while (a != p[a]) {
ans1++;
a = p[a];
}
while (b != p[b]) {
ans2++;
b = p[b];
}
if (ans1 > ans2) {
cout << "pfz" << endl;
}
else {
cout << "lxh" << endl;
}
}
}
return 0;
}
HDU-2544 最短路
样例输入:
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
样例输出:
3
2
最短路板子题,N为100,数据范围很小,可以用最简单写的多源最短路Floyd,时间复杂度 O(N^3)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 110;
int n, m;
int dis[N][N];
void floyd()
{
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
}
int main()
{
while (cin >> n >> m) {
if (!n && !m) {
break;
}
memset(dis, 0x3f, sizeof(dis));
while (m--) {
int a, b, c;
cin >> a >> b >> c;
dis[a][b] = dis[b][a] = min(dis[a][b], c);
}
floyd();
cout << dis[1][n] << endl;
}
return 0;
}
需要注意考虑重边和无向边(第28行代码)
HDU-2066 一个人的旅行
样例输入:
6 2 3
1 3 5
1 4 7
2 8 12
3 8 4
4 9 12
9 10 2
1 2
8 9 10
样例输出:
9
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1010;
int g[N][N];
int m, s, d;
int want[N], link[N];
bool st[N];
int dis[N];
void dij(int s ,int n)
{
memset(dis, 0x3f, sizeof(dis));
memset(st, 0, sizeof(st));
dis[s] = 0;
for (int i = 0; i < n; i++) {
int t = -1;
for (int j = 1; j <= n; j++) {
if (!st[j] && (t == -1 || dis[j] < dis[t])) {
t = j;
}
}
st[t] = 1;
for (int j = 1; j <= n; j++) {
dis[j] = min(dis[j], dis[t] + g[t][j]);
}
}
}
int main()
{
while (cin >> m >> s >> d) {
memset(g, 0x3f, sizeof(g));
int a, b, c;
int ma = 0;
while (m--) {
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b], c);
ma = max(ma, max(a, b));
}
for (int i = 0; i < s; i++) {
cin >> link[i];
}
for (int i = 0; i < d; i++) {
cin >> want[i];
}
int ans = 0x3f3f3f3f;
for (int i = 0; i < s; i++) {
dij(link[i], ma);
for (int j = 0; j < d; j++) {
ans = min(ans, dis[want[j]]);
}
}
cout << ans << endl;
}
return 0;
}
HDU-1233 还是畅通工程
样例输入:
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
样例输出:
3
5
最小生成树板子题 用Kruskal算法
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 110, M = N * (N - 1) / 2;
int p[N];
int n, m;
int a, b, w;
int find(int x)
{
if (x != p[x]) {
p[x] = find(p[x]);
}
return p[x];
}
struct S
{
int a, b, w;
}s[M];
int cmp(S a, S b)
{
return a.w < b.w;
}
int kruskal()
{
int ans = 0;
for (int i = 1; i <= n; i++) {
p[i] = i;
}
sort(s, s + m, cmp);
for (int i = 0; i < m; i++) {
int a = find(s[i].a);
int b = find(s[i].b);
if (a != b) {
p[a] = b;
ans += s[i].w;
}
}
return ans;
}
int main()
{
while (scanf("%d",&n)) {
if (!n) {
break;
}
memset(s, 0, sizeof(s));
memset(p, 0, sizeof(p));
m = n * (n - 1) / 2;
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &a, &b, &w);
s[i] = { a,b,w };
}
cout << kruskal() << endl;
}
return 0;
}
HDU-2546饭卡
样例输入:
1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0
样例输出:
-45
32
先拿出5块买最贵的菜,然后再用剩下的钱买其它菜要花的最大价值,这就转换成了01背包问题
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, k;
int arr[N];
int f[N];
int main()
{
while (cin >> n) {
if (!n) {
break;
}
memset(arr, 0, sizeof(arr));
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; i++) {
cin >> arr[i];
}
cin >> k;
if (k < 5) {
cout << k << endl;
continue;
}
sort(arr + 1, arr + 1 + n);
int m = k - 5;
for (int i = 1; i < n; i++) {
for (int j = m; j >= arr[i]; j--) {
f[j] = max(f[j], f[j - arr[i]] + arr[i]);
}
}
cout << k - f[m] - arr[n] << endl;
}
return 0;
}
HDU-1166 敌兵布阵
样例输入:
1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End
样例输出:
Case 1:
6
33
59
题目嘎嘎长,其实就是动态的单点修改,区间求和 😮 😮 😮
树状数组登场 😍 😍 😍
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
const int N = 5e4 + 10;
int tree[N];
int n;
string s;
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i < N; i += lowbit(i)) {
tree[i] += v;
}
}
int query(int x)
{
int ans = 0;
for (int i = x; i; i -= lowbit(i)) {
ans += tree[i];
}
return ans;
}
int main()
{
int t;
cin >> t;
int k = 1;
while (t--) {
memset(tree, 0, sizeof(tree));
cout << "Case " << k++ << ":" << endl;
int x;
cin >> n;
for (int i = 1; i <= n; i++) {
scanf_s("%d", &x);
add(i, x);
}
int a, b;
while (cin >> s) {
if (s == "End") {
break;
}
scanf_s("%d%d", &a, &b);
if (s == "Add") {
add(a, b);
}
else if (s == "Sub") {
add(a, -b);
}
else {
cout << query(b) - query(a - 1) << endl;
}
}
}
return 0;
}
HDU-2063过山车
样例输入:
6 3 3
1 1
1 2
1 3
2 1
2 3
3 1
0
样例输出:
3
二分图匹配---匈牙利算法
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 510;
int match[N];
int h[N], ne[N * N], idx, e[N * N];
int st[N];
int m, x1, x2;
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
bool dfs(int u)
{
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (!st[j]) {
st[j] = 1;
if (!match[j] || dfs(match[j])) {
match[j] = u;
return 1;
}
}
}
return 0;
}
int main()
{
while (cin >> m) {
if (!m) {
break;
}
cin >> x1 >> x2;
memset(h, -1, sizeof(h));
memset(match, 0, sizeof(match));
int a, b;
while (m--) {
cin >> a >> b;
add(a, b);
}
int ans = 0;
for (int i = 1; i <= x1; i++) {
memset(st, 0, sizeof(st));
if (dfs(i)) {
ans++;
}
}
cout << ans << endl;
}
return 0;
}