思路:简单的dfs,因为只用一个分支,当再次回来的时候就是结果。
代码:
#include
using namespace std;
const int maxn = 1005;
int a[maxn], tot = 0;
int n, vis[maxn], ans = 0;
int dfs(int u)
{
vis[u] = 1;
int t = a[u];
if(!vis[t])
dfs(t);
else return t;//如果回到之前标记的就是结果
}
int main()
{
int v;
scanf("%d", &n);
tot = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++)
{
memset(vis, 0, sizeof(vis));
ans = 0;
if(i != 1)printf(" ");
printf("%d", dfs(i));
}
printf("\n");
return 0;
}
题意:就是给你一个图,图上有羊和狼,不让狼吃羊,你必须在用狗去把狼隔开,注意不用考虑放置狗的个数最少You don’t have to minimize the number of dogs。
思路:既然不用考虑够的个数最少,那么只需要,判断羊的四周是否是狼,如果是狼那么就输出NO,否则输出YES以及放置后的地图。
代码:
#include
using namespace std;
const int maxn = 505;
char mp[maxn][maxn];
int vis[maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0}, n, m;
int flag = 1;
int main(){
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++){
scanf("%s", mp[i]);
}
flag = 1;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(mp[i][j] == 'S'){
if(mp[i][j-1] == 'W'|| mp[i-1][j] == 'W' ||mp[i+1][j] == 'W'|| mp[i][j+1] == 'W'){
printf("NO\n");
return 0;
}
}
}
}
printf("YES\n");
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(mp[i][j] == '.')printf("D");
else printf("%c", mp[i][j]);
}
printf("\n");
}
return 0;
}
题意:本题的大体思路就是给你三个点(ax, ay),(bx, by),(cx, cy),问你从(bx,by)这个点能否不经过与(ax, ay)同行同列同一对角线到达(cx, cy),如果能输出YES,否则输出NO。
思路:本题的主体思路就是类似与dfs走迷宫的那类题,但是本题需要加入一些判断,使走过的路线不与(ax, ay)同行同列同一对角线,不同行不同类好比较。但是不同对角线如何比较,通过观察图发现,同一对角线斜路是-1或者1.然后在通过y = x+b或者y = -x+b,把(ax, ay)这个点带入,就能求出b,然后根据方程判断是否与(ax, ay)在同一对角线上。
本题还可以以(ax,by)作为原点建立直角坐标系,如果(bx,by)和(cx,cy)在同一象限那么就输出YES,,否则输出NO。为什么可以这么做你可以看图找一下规律。
参考博客:CF1033A. King Escape的题解
代码:
#include
using namespace std;
const int maxn = 1010;
int ax, ay, bx, by, cx, cy, n, b1, b2;
bool Judge1(int x, int y){
if(y == x+b1)return true;
else return false;
}
bool Judge2(int x, int y){
if(y == -x+b2)return true;
else return false;
}
int dx[10] = {0,0,1,1,1,-1,-1,-1};//八个方向
int dy[10] = {1,-1,1,-1,0,1,-1,0};
int vis[maxn][maxn];//标记
int flag = 0;
bool dfs(int x, int y){
vis[x][y] = 1;
if(x == cx && y == cy){//到达终点
flag = 1;
return true;
}
for(int i = 0; i < 8; i++){
int tx, ty;
tx = x+dx[i];
ty = y+dy[i];
if(tx >= 1 && tx <= n && ty >= 1 && ty <= n && !vis[tx][ty] && !Judge1(x, y) && !Judge2(x, y) && tx != bx && ty != by){//判断
vis[tx][ty] = 1;
if(dfs(tx, ty))return true;//递归
}
}
return false;
}
int main(){
memset(vis, 0, sizeof vis);
scanf("%d", &n);
scanf("%d %d", &bx, &by);
scanf("%d %d", &ax, &ay);
scanf("%d %d", &cx, &cy);
b1 = by-bx;
b2 = bx+by;
int t = dfs(ax, ay);
if(t)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;
}
题意:本题的题意就是给你i点可以到大(i+ai),问你是否能到大K,如果能到达输出YES,否则话输出NO。因为本题已经约束了1
代码:
#include
using namespace std;
const int maxn = 3e4+10;
int a[maxn], n, k;
bool dfs(int x){
if(x > k)return false;//因为不能回退,所以当x>k时就已经没有搜到
if(x == k)return true;
dfs(x+a[x]);
//return false;
}
int main(){
scanf("%d %d", &n, &k);
for(int i = 1; i < n; i++)scanf("%d", &a[i]);
int t = dfs(1);
if(t)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;
}
题意:给你两个数a, b,让你通过一两种操作把a变成b,这两中操作分别是x–>2x, x–>x10+1;通过这两种操作如果不能变成b输出NO,如果能就输出YES 操作数 变换的过程
思路:一个思路就是简单的dfs,另外一个就是根据性质去推从b往前推a,如果b是偶数那么b就除以2,如果b为奇数那么就是通过10*x+1这个步骤。
参考博客:CodeForces–TechnoCup–2016.10.15–ProblemA–Transformation: from A to B
dfs代码:
#include
using namespace std;
const int maxn = 1005;
#define LL long long
const int INF = 100000;
int vis[maxn], val[maxn],ans = INF;//val存储路径
LL a, b;
void dfs(LL x, LL step){
if(x == b){//如果到达终点
if(step < ans){//步数小于当前的步数
for(int i = 0; i < step; i++){//更新
val[i] = vis[i];
}
ans = step;
}
return;
}
if(x*2 <= b){//乘2的操作
vis[step] = x*2;
dfs(x*2, step+1);
vis[step] = 0;
}
if(x * 10 + 1 <= b){//后面补1的操作
vis[step] = x*10+1;
dfs(x*10+1, step+1);
vis[step] = 0;
}
}
int main(){
scanf("%lld %lld", &a, &b);
dfs(a, 0);
if(ans == INF)printf("NO\n");
else {
printf("YES\n");
printf("%d\n", ans+1);
printf("%d", a);
for(int i = 0; i < ans; i++){
printf(" %d", val[i]);
}
printf("\n");
}
return 0;
}
题意:本题的题意就是给你一个长度为n的字符串,让你从1开始走,走到n,每次走的距离不能超过d,而且每次走的时候只能走数值为1的点,如果可以到达n,输出结果最小步数,否则输出-1。
思路:本题有两种思路一个是dfs,一个数模拟,先说dfs吧,首先我设当前结点为x,然后从k最小开始dfs,然后在到达n点的时候去更新最小值,后来发现跑到第15组就超时了,我感觉这种方法存在问题,从k最大开始搜索这么就不用去更新最小值了,因为这么做一定是最优的,结果跑到44组就超时了,虽然有一定的优化但是还是存在一定问题,我就想可不可以用数组去标记,仔细思考了一下,发现是可以的,为什么是可以的如果我把当前结点标记了如果搜索失败在回溯的时候,如果已经标记了那么证明从该点的路径是不可达的,所以不能走。
模拟的思想其实也差不多,每一次尽量走最大的如果不能走就往前挪一下,重复上述操作。参考博客:The Way to Home (模拟)
dfs代码:
#include
using namespace std;
const int maxn = 1000;
char str[maxn];
int n, k, ans = maxn;
bool vis[maxn];
bool dfs(int x, int step){
if(x == n){//到达n点
ans = step;
return true;
}
for(int i = k; i > 0; i--){
if(str[x+i] == '1' && !vis[x+i]){
vis[x+i] = 1;
if(dfs(x+i, step+1))return true;
}
}
return false;
}
int main(){
scanf("%d %d", &n, &k);
scanf("%s", str+1);
ans = maxn;
dfs(1,0);
if(ans == maxn)printf("-1\n");
else printf("%d\n", ans);
return 0;
}
题目:本题题意就是给你一个矩阵,"."代表可以放置棋子的,“-”代表不可以放置棋子,然后让你把所有相邻的可以放置棋子的位置放置不同的颜色的棋子,如果只能放置一个棋子的这种情况所以放置。
思路:就是一个迷宫类型的搜索的简单变形,只需要增加一个变量就可以,用它来记录之前的情况。
代码:
#include
using namespace std;
const int maxn = 105;
char a[maxn][maxn];
bool vis[maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1,0, 0};
int n, m;
void dfs(int x, int y, int flag)
{
if(!flag)a[x][y] = 'B';
else a[x][y] = 'W';
for(int i = 0; i < 4; i++)
{
int tx, ty;
tx = x + dx[i];
ty = y + dy[i];
if(tx >= 0 && tx < n && ty >=0 && ty < m && a[tx][ty] == '.')
{
dfs(tx,ty,!flag);
}
}
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
{
scanf("%s", a[i]);
}
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(a[i][j] == '.')dfs(i, j, 0);
}
}
for(int i = 0; i < n; i++)printf("%s\n", a[i]);
return 0;
}
题意:本题的题意就是你通过取消只与一个点相连所有点,然后在重复操作,统计你的操作个数。
思路:本题就是一个拓扑排序,因为是无向图,所以所有点都是入度,然后找到入度为1的点。
代码:
#include
using namespace std;
const int maxn = 105;
int a[maxn][maxn];
bool vis[maxn][maxn];
int n, m;
int degree[maxn];
queue<int>q;
int cn = 1, ans = 0, tmp;
void TopSort(int n)
{
int cnt = 0;
for(int i = 1; i <= n; i++)
{
if(degree[i] == 1)
{
q.push(i);
cnt++;
}
}
if(cnt)ans++;//如果存在入度为1的点,操作次数加一
while(!q.empty())
{
tmp = q.front();
q.pop();
for(int i = 1; i <= n; i++)
{
if(a[tmp][i])
{
degree[i]--;
degree[tmp]--;
}
}
cnt--;
if(cnt == 0)//如果第一次的都已经完事了
{
for(int i = 1; i <= n; i++)//开始下一步
{
if(degree[i] == 1)
{
q.push(i);
cnt++;
}
}
if(cnt)ans++;//如果下一步存在,结果加一
}
}
}
int main()
{
int u, v;
ans = 0;
scanf("%d %d", &n, &m);
for(int i = 0; i < m; i++)
{
scanf("%d %d", &u, &v);
a[u][v] = a[v][u] = 1;
degree[u]++;
degree[v]++;
}
TopSort(n);
cout<<ans<<endl;
return 0;
}
题意:本题的题意就是把不同的不存在上级的人放在同一组中。
思路:本题的思想非常简单,就是几个建图加上拓扑排序就完事了,如果存在没有上级的那种就不用处理了。
代码:
#include
using namespace std;
const int maxn = 2005;
int degree[maxn];
int mp[maxn][maxn];
struct Node{
int id, x;
}tmp;
int ans = 0, n;
queueq;
void TopSort(){//拓扑排序
for(int i = 1; i <= n; i++){
if(!degree[i]){
tmp.id = 1;
tmp.x = i;
q.push(tmp);
}
}
while(!q.empty()){
tmp = q.front();
int x = tmp.x;
int id = tmp.id;
q.pop();
ans = max(ans, id);
for(int i = 1; i <= n; i++){
if(mp[x][i]){
degree[i]--;
if(!degree[i]){
tmp.x = i;
tmp.id = id+1;
q.push(tmp);
}
}
}
}
}
int main(){
int t;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d",&t);
if(t == -1)continue;//不存在上级的这种情况
mp[t][i] = 1;//反向建图
degree[i]++;
}
TopSort();
printf("%d\n", ans);
return 0;
}
题意:就是告诉你贿赂每一个人要多少钱,朋友之间可以免费互传消息,所以问你最小的花费。
思路:本题可以利用并查集写,也可以用dfs写。我只列出dfs代码。
并查集代码:参考博客:codeforces 893 C.Rumor
dfs代码:
#include
using namespace std;
const long long INF = 1e9+1;
const int maxn = 1e5+10;
int vis[maxn], val[maxn];
int ne[maxn*2], e[maxn*2],h[maxn*2], tot = 0;
void add(int u, int v)//链式前向星存储,注意无向图,所以需要开2*maxn
{
e[tot] = v;
ne[tot] = h[u];
h[u] = tot++;
}
int ans;
void dfs(int u)//搜索
{
vis[u] = 1;
ans = min(ans, val[u]);
for(int i = h[u]; i != -1; i = ne[i])
{
int t = e[i];
if(!vis[t])
{
dfs(t);
}
}
}
int main()
{
int n, m, u, v;
scanf("%d %d", &n, &m);
memset(h, -1, sizeof(h));
for(int i = 1; i <= n; i++)scanf("%d", &val[i]);
for(int i = 1; i <= m; i++)
{
scanf("%d %d", &u, &v);
add(u, v);
add(v,u);
}
long long sum = 0;
for(int i = 1; i <= n; i++)
{
ans = INF;
if(!vis[i])//如果没有搜索过
{
dfs(i);
sum += ans;
}
}
printf("%lld\n", sum);
return 0;
}
题意:本题的题意就是给你一个图,当这张图上当且仅当只有一个由* 围成的+号,并且除了这个加好的四个方向上有*, 其他地方不能有 *这种情况下输出YES,否则输出NO。
思路:本题就是一个暴力,首先找到中心点如果找到仅找到一个中心点,那么就以这个点为中心向四周扩展,标记扩展经过的点,然后暴力一遍,有没有没有经过的点,那么就是NO,没有的话就是YES,找到多个或者没有找到中心点也是NO。
代码:
#include
using namespace std;
const int maxn = 505;
char mp[maxn][maxn];
int vis[maxn][maxn];
int n, m;
int main()
{
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
{
scanf("%s", &mp[i]);
}
int st, en, flag = 0;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(mp[i][j] == '*' && i > 0 && i < n-1 && j > 0 && j < m-1)//找中心点
{
if(mp[i][j-1] == '*' && mp[i-1][j] == '*' && mp[i][j+1] == '*' && mp[i+1][j] == '*')
{
flag++;
st = i;
en = j;
}
}
}
}
if(flag == 1)
{
vis[st][en] = 1;//别忘了把中心点标记一下
for(int i = st+1; mp[i][en] == '*'; i++)vis[i][en] = 1;
for(int i = st-1; mp[i][en] == '*'; i--)vis[i][en] = 1;
for(int i = en+1; mp[st][i] == '*'; i++)vis[st][i] = 1;
for(int i = en-1; mp[st][i] == '*'; i--)vis[st][i] = 1;
int k = 0;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(mp[i][j] == '*' && !vis[i][j]){
k = 1;
break;
}
}
}
if(k)cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
else cout<<"NO"<<endl;
return 0;
}
题意:本题的题意就是给你一颗树,每一个节点都应该染成他所需要的颜色,但是在染色的同时会同时把他的子树给染色了,问你需要多少步,把这个树变成你需要的树的颜色。
思路:本题的思路就是从根节点开始染色,把所有的子节点染成需要的颜色。
代码:
#include
using namespace std;
const int maxn = 1e4+10;
int vis[maxn], color[maxn];
int head[maxn], nx[maxn], e[maxn], tot = 0;
void add(int u, int v){
e[tot] = v;
nx[tot] = head[u];
head[u] = tot++;
}
void dfs(int x, int cr){
vis[x] = cr;
for(int i = head[x]; i != -1; i = nx[i]){
int t = e[i];
dfs(t, cr);
}
}
int main(){
int n, v;
memset(vis, 0, sizeof vis);
memset(head, -1, sizeof head);
scanf("%d", &n);
for(int i = 2; i <= n; i++){
scanf("%d", &v);
add(v, i);
}
int step = 0;
for(int i = 1; i <= n; i++)scanf("%d", &color[i]);
for(int i = 1; i <= n; i++){//这个需要根据结果去推一下
if(vis[i] == color[i])continue;
else {
step++;
dfs(i, color[i]);
}
}
cout<<step<<endl;
return 0;
}
或者:
#include
using namespace std;
queue<int>q;
const int maxn = 1e4+10;
int vis[maxn], color[maxn];
int head[maxn], nx[maxn], e[maxn], tot = 0;
void add(int u, int v)
{
e[tot] = v;
nx[tot] = head[u];
head[u] = tot++;
}
void dfs(int x, int cr)
{
vis[x] = cr;
for(int i = head[x]; i != -1; i = nx[i])
{
int t = e[i];
dfs(t, cr);
}
}
int main()
{
int n, v;
memset(vis, 0, sizeof vis);
memset(head, -1, sizeof head);
scanf("%d", &n);
for(int i = 2; i <= n; i++)
{
scanf("%d", &v);
add(v, i);
}
int step = 0;
for(int i = 1; i <= n; i++)scanf("%d", &color[i]);
q.push(1);
while(!q.empty())
{
int t = q.front();
for(int i = head[t]; i != -1; i = nx[i])
{
q.push(e[i]);
}
q.pop();
if(vis[t] == color[t])continue;
else
{
step++;
dfs(t, color[t]);
}
}
cout<<step<<endl;
return 0;
}
由于是最短的所以bfs更好。
代码:
#include
using namespace std;
int n, m, ans = 0;
int vis[10010];
queue<int>q;
int dfs(int x)
{
q.push(x);
vis[x] = 0;
while(q.size()){
int t = q.front();
q.pop();
if(t == m)return vis[m];
int xx = t * 2;
if(xx <= 10000 && !vis[xx]){
vis[xx] = vis[t] + 1;
q.push(xx);
}
xx = t-1;
if(xx > 0 && !vis[xx]){
vis[xx] = vis[t] + 1;
q.push(xx);
}
}
}
int main()
{
scanf("%d %d", &n, &m);
ans = 1e7;
ans = 0;
if(n > m)
{
cout<<n-m<<endl;
}
else
{
cout<<dfs(n)<<endl;
}
return 0;
}
思路:就是求出几个互相联通的集合,集合的数量减去1就是答案。
代码:
#include
using namespace std;
const int maxn = 1010;
int x[maxn], y[maxn];
int n, vis[maxn];
void dfs(int i){
vis[i] = 1;
for(int j = 1; j <= n; j++){
if(!vis[j] && (x[i] == x[j] || y[i] == y[j])){
dfs(j);
}
}
}
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%d %d", &x[i], &y[i]);
}
int ans = 0;
for(int i = 1; i <= n; i++){
if(!vis[i])
dfs(i), ans++;
}
printf("%d\n", ans-1);
return 0;
}