题目1 : 树结构判定
描述
给定一个包含 N 个顶点 M 条边的无向图 G ,判断 G 是不是一棵树。
输入
第一个是一个整数 T ,代表测试数据的组数。 (1 ≤ T ≤ 10)
每组测试数据第一行包含两个整数 N 和 M 。(2 ≤ N ≤ 500, 1 ≤ M ≤ 100000)
以下 M 行每行包含两个整数 a 和 b ,表示顶点 a 和顶点 b 之间有一条边。(1 ≤ a, b ≤ N)
输出
对于每组数据,输出YES或者NO表示 G 是否是一棵树。
解析:一个图是一棵树,只要满足两个条件:1.边的条数等于顶点个数减1;2.每一个顶点的度都不为0;
源码:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n, m, T, v1, v2;
vector<int> degrees;
cin >> T;
while (T--)
{
cin >> n >> m;
degrees.assign(n, 0);
for (int i = 0; i < m; i++)
{
cin >> v1 >> v2;
degrees[v1 - 1]++;
degrees[v2 - 1]++;
}
if (m != n - 1){ cout << "NO\n"; continue; }
int j;
for (j = 0; j < n;j++)
if (degrees[j] == 0) { cout << "NO\n"; break; }
if (j == n) cout << "YES\n";
}
return 0;
}
描述
给定一个字符串 S ,最少需要几次增删改操作可以把 S 变成一个回文字符串?
一次操作可以在任意位置插入一个字符,或者删除任意一个字符,或者把任意一个字符修改成任意其他字符。
输入
字符串 S。S 的长度不超过100, 只包含'A'-'Z'。
输出
最少的修改次数。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
vector<vector<int>> Record;
int Recursion(string& str, int begin, int end)
{
if (begin > end) return 0;
if (begin == end){ Record[begin][end] = 0; return 0; }
if (Record[begin][end]) return Record[begin][end];
int res;
if (str[begin] == str[end])
res = Recursion(str, begin + 1, end - 1);
else
{
int val1 = Recursion(str, begin, end - 1);
int val2 = Recursion(str, begin + 1, end);
int val3 = Recursion(str, begin + 1, end - 1);
res = min(val1, min(val2, val3)) + 1;
}
Record[begin][end] = res;
return res;
}
int main()
{
string str;
cin >> str;
int n = str.length();
Record.assign(n, vector<int>(n, 0));
Recursion(str, 0, n - 1);
cout << Record[0][n-1] << endl;
return 0;
}
描述
希尔伯特曲线是以下一系列分形曲线 Hn 的极限。我们可以把Hn 看作一条覆盖 2n × 2n 方格矩阵的曲线,曲线上一共有 2n × 2n 个顶点(包括左下角起点和右下角终点),恰好覆盖每个方格一次。
Hn(n > 1)可以通过如下方法构造:
1. 将 Hn-1 顺时针旋转90度放在左下角
2. 将 Hn-1 逆时针旋转90度放在右下角
3. 将2个 Hn-1 分别放在左上角和右上角
4. 用3条单位线段把4部分连接起来
对于 Hn 上每一个顶点 p ,我们定义p 的坐标是它覆盖的小方格在矩阵中的坐标,定义p 的序号是它在曲线上从起点开始数第几个顶点。给定 p 的坐标,你能算出p 的序号吗?
输入
输入包含3个整数 n , x , y 。 n 是分形曲线的阶数,(x,y)是p 的坐标。
1 ≤ n ≤ 30
1 ≤ x, y ≤ 2n
输出
p 的序号。
解析:对于阶数为n的矩阵,我们可以看到其希尔伯特曲线总体走势是按顺时针,将其分为四块,每块大小为2^(n-1)x2^(n-1),分别记左下、左上、右上、右下四块为1、2、3、4;首先根据p点的坐标,可以判断出他在那个区,假如它在3区,那么可以将1,2区中全部块数计入到p点序号数中去;然后我们将3区取出(即为阶数为n-1的矩阵),并将p点坐标映射到阶数为n-1的矩阵中,就可以按上述方法进行计算;依次作一个循环,将每个循环中覆盖到的块数加起来,即为p点序号数。
当然还要注意曲线是按顺时针走还是按逆时针走,比如阶数为n的矩阵1区转化为n-1阶矩阵时,其曲线走势是逆时针方向,此时若p点落在3区,需要加上4区的块数。
最后说一下坐标映射,p点在阶数为n矩阵中坐标为(x,y),令N=2^(n-1),则:
p点在阶数为n矩阵中所在区 p点在阶数为n-1的矩阵中坐标
1 (N+1-y,x)
2 (x,y-N)
3 (x-N,y-N)
4 (y,2*N+1-x)
源代码:
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
int n, x, y;
cin >> n >> x >> y;
int flag = 1, temp; //曲线走势为顺时针时flag为1,走势为逆时针时flag为0
long long num = 0, N;
for (int i = n; i >= 1; i--)
{
N = pow(2, i - 1);
if (x <= N)
{
if (y <= N) //p点落在1区
{
num += (1 - flag) * 3 * N*N;
temp = y;
y = x;
x = N + 1 - temp;
flag = 1 - flag;
}
else //p点落在2区
{
num += (flag + (1 - flag) * 2)*N*N;
y -= N;
}
}
else
{
if (y <= N) //p点落在4区
{
num += flag * 3 * N*N;
temp = x - N;
x = y;
y = N + 1 - temp;
flag = 1 - flag;
}
else //p点落在3区
{
num += (2 * flag + (1 - flag))*N*N;
x -= N;
y -= N;
}
}
if (i == 1) num++;
}
cout << num << endl;
return 0;
}