文章目录
A - Launch of Collider
- 思路:水题。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 200005;
const int INF = 1<<29;
int a[MAX];
char s[MAX];
int main()
{
int n;
scanf("%d", &n);
scanf("%s", s);
int Min = INF;
for(int i=0; i<n; i++)
{
scanf("%d", &a[i]);
if(i==0) continue;
if(s[i]=='L' && s[i-1]=='R')
{
int t = (a[i]-a[i-1])/2;
if(Min>t) Min = t;
}
}
if(Min==INF) printf("-1\n");
else printf("%d\n", Min);
return 0;
}
B - One Bomb
题目链接:B - One Bomb
- 思路:原来的思路过于复杂,代码太臃肿。虽然过了,但是思维提升空间很大。网上的思路较为精简,首先记录每一行每一列的墙的数目,然后在遍历每一个结点,如果该结点的同行和同列的墙的数目之和减去(如果该位置时墙则为1,否则为0),等于墙的总数,则YES,当前位置则为输出,不存在则NO。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
string G[1005];
int row[1005], col[1005];
int main()
{
int n, m, sum = 0;
cin >> n >> m;
memset(row, 0, sizeof(row));
memset(col, 0, sizeof(col));
for(int i=1; i<=n; i++)
{
cin >> G[i];
G[i] = " "+G[i];
for(int j=1; j<=m; j++)
{
if(G[i][j]=='*')
{
sum++;
row[i]++;
col[j]++;
}
}
}
int x = -1, y = -1;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
int cnt = row[i]+col[j];
if(G[i][j]=='*') cnt--;
if(cnt==sum)
{
x = i, y = j;
break;
}
}
if(x!=-1) break;
}
if(x==-1) cout << "NO" << endl;
else
{
cout << "YES" << endl;
cout << x << " " << y << endl;
}
return 0;
}
C - Vacations
题目链接:C - Vacations
- 思路:这道DP题的状态定义的好特别。
- 状态构造:dp[i][k]表示在第i天选择第k个选择的最小休息天数。k=0,1,2。
- 终态:min(dp[n][0], min(dp[n][1], dp[n][2]))。
- 状态转移方程:
- dp[i][1]=min(dp[i-1][0],dp[i-1][2]) , a[i]==1||3 ,否则为INF
- dp[i][2]=min(dp[i-1][0],dp[i-1][1]) ,a[i]==2||3,否则为INF
- dp[i][0]=min(dp[i-1][0],dp[i-1][1],dp[i-1][2])+1, 任何情况
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 1<<29;
const int MAX = 200;
int a[MAX], dp[MAX][5];
int main()
{
int n;
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
}
memset(dp, 0, sizeof(dp));
for(int i=1; i<=n; i++)
{
dp[i][0] = min(dp[i-1][0], min(dp[i-1][1], dp[i-1][2]))+1;
if(a[i]==1 || a[i]==3)
dp[i][1] = min(dp[i-1][0], dp[i-1][2]);
else
dp[i][1] = INF;
if(a[i]==2 || a[i]==3)
dp[i][2] = min(dp[i-1][0], dp[i-1][1]);
else
dp[i][2] = INF;
}
printf("%d\n", min(dp[n][0], min(dp[n][1], dp[n][2])));
return 0;
}
D - Fix a Tree
题目链接:D - Fix a Tree
参考博文:Codeforces Round #363 (Div. 2) D. Fix a Tree —— 并查集
- 思路:本题实质上是将无向图转换为一棵树,对于树一定没有环,且是一个连通图。而输入是一些结点的父结点,一个结点有且仅有一个父结点。所以仅有两种情况的环,一个是自身是自己的父结点,一个是自己的子孙结点是自己的父结点,且不会有两个相连的环。
所以可以使用并查集找出各个集合的根,如果存在某个集合的根的原始父结点是自身,则可以将该结点作为整个树的根(因为不需要改该结点的父节点了),然后将其他集合的根的父节点更改为该结点。如果不存在这样的结点,则选出一个集合的根,将所有集合的根(包括该结点)的父结点更改为该结点。这样一定可以去除环,并形成一个连通图。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX = 200005;
int father[MAX], a[MAX];
void Init(int n)
{
for(int i=1; i<=n; i++)
father[i] = i;
}
int findRoot(int x)
{
int r = x;
while(father[r]!=r)
{
r = father[r];
}
int j = x, i;
while(j!=r)
{
i = father[j];
father[j] = r;
j = i;
}
return r;
}
void join(int x, int y)
{
int fx = findRoot(x), fy = findRoot(y);
if(fx!=fy)
father[fx] = fy;
}
bool same(int x, int y)
{
return findRoot(x)==findRoot(y);
}
int main()
{
int n;
scanf("%d", &n);
Init(n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
if(!same(i, a[i]))
join(i, a[i]);//一定是a[i]作为i的父亲
}
int cnt = 0, root = 0;//cnt是改变的次数,root是根节点
for(int i=1; i<=n; i++)//选出根节点
{
if(father[i]==i && a[i]==i)
{
root = i;
break;
}
}
for(int i=1; i<=n; i++)
{
if(father[i]==i)
{
if(!root)
root = i, a[i] = i, cnt++;
else if(i!=root)
a[i] = root, cnt++;
}
}
printf("%d\n", cnt);
for(int i=1; i<=n; i++)
{
if(i>1)
printf(" ");
printf("%d", a[i]);
}
printf("\n");
return 0;
}
E - LRU
题目链接: E - LRU