CFR 363
给定一个序列:
ai equals 0, if on the i-th day of vacations the gym is closed and the contest is not carried out;
ai equals 1, if on the i-th day of vacations the gym is closed, but the contest is carried out;
ai equals 2, if on the i-th day of vacations the gym is open and the contest is not carried out;
ai equals 3, if on the i-th day of vacations the gym is open and the contest is carried out.
还有个要求:他不会连续两(>=2)天都做相同的运动: (=_=反正就是做活动要间隔像1,2,1,2,1,2)
问最少能休息几天。
dp[i][j]表示第 i 天做 j 类型的活动所能休息的最少时间。
如果x为1,则说明当天能休息或者去GYM,所以更新dp[ i ][1]和dp[ i ][0],同时当天不能做2类型的运动,所以dp[i][2]=INF;其他类型同理,递推下去就好了
/*cf 698A*/
#include<stdio.h>
#include<algorithm>
using namespace std;
int D[1005][3];
int main()
{
//freopen("in.txt","r",stdin);
int x,N;
scanf("%d", &N);
for (int i = 1; i <= N; i++)
{
scanf("%d",&x);
if (x==3)
{
D[i][0] = min(min(D[i - 1][0], D[i - 1][1]),D[i-1][2])+1;
D[i][1] = min(D[i - 1][0], D[i - 1][2]);
D[i][2] = min(D[i - 1][0], D[i - 1][1]);
}
if (x==2)
{
D[i][0] = min(min(D[i - 1][0], D[i - 1][1]), D[i - 1][2]) + 1;
D[i][2] = min(D[i - 1][0], D[i - 1][1]);
D[i][1] = 100000000;
}
if (x==1)
{
D[i][0] = min(min(D[i - 1][0], D[i - 1][1]), D[i - 1][2]) + 1;
D[i][2] = 100000000;
D[i][1] = min(D[i - 1][0], D[i - 1][2]);
}
if (x==0)
{
D[i][0] = min(min(D[i - 1][0], D[i - 1][1]), D[i - 1][2]) + 1;
D[i][1] = 100000000;
D[i][2] = 100000000;
}
}
printf("%d\n", min(min(D[N][0], D[N][1]), D[N][2]));
return 0;
}
CF 698B
给一个数组a,
ai是 i 的父亲。
让你修改最少的元素pi让这数组变成一棵合法的树…
首先,要找出这个数组中有多少个环和多少个根节点( ai== i ) 的.
然后将每个环中的一个点当作这个环的根节点。
然后使用并查集解法是:
先把所有根节点都找出来,并将总的根节点num初始为其中的一个根节点。
再把所有的环都拆开,找出环中的一个点作为根节点a[i]==i。
然后把所有根结点都连向num。
注意: 优先把总根节点num初始化成a[i]==i,而不是拆开的环,因为要找的是操作数最少,找环的话操作次数会+1
这方法也是从CF一个看到的代码那里学习的…
我本来想把这棵树分成几个联通量…再把所有联通量都找出根节点,一 一连起来..
但是不知道怎么把环拆开…
/*并查集*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200050;
int a[maxn],f[maxn],ans[maxn];
int Find(int x)
{
return f[x] == x?x:f[x] = Find(f[x]);
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif // LOCAL
int n;
scanf("%d",&n);
for(int i = 1; i<=n; i++) f[i] = i;
int cnt=0,num=-1;;
for(int i = 1; i<=n; i++)
{
scanf("%d",&a[i]);
int x=Find(i),y=Find(a[i]);
if(a[i]==i) {num=i,ans[++cnt] = i;continue;}//这是一个根节点
else if(x==y)//ai!=i,而i,a[i]在一个联通块里,此时又要将i连向a[i],所以肯定会出现环。
{
a[i]=i;//将所有环都拆开,并以这个点为根节点。
cnt++;
}
f[x]=y;
}
if(num==-1)//如果没有找到根节点,再从拆开的环里面找出一个结点当根节点。
{
for(int i=1;i<=n;i++)
if(a[i]==i)
{
num=i;
cnt++;//只有环的话,改变的元素会多一个。
break;
}
}
printf("%d\n",--cnt);
for(int i=1;i<=n;i++)
{
if(i==a[i]) a[i]=num;//将所有根节点都连向一个根节点num
printf("%d%c",a[i],i==n?'\n':' ');
}
}
BFS解法…其实和并查集差不多..
只是我把每个联通量的根节点都存了起来,再把所有根节点都连向root…
就是BFS判环和并查集判环不同。
/*BFS*/
#include <bits/stdc++.h>
using namespace std;
int f[200006];
int circle[200006],cnt,fa[200006];
bool vis[200006];
vector<int >E[200006];
int num=-1,flag=0;
int bfs(int x)
{
queue<int>Q;
Q.push(x);
int t=0;
bool cir=0;
while(!Q.empty())
{
x=Q.front();
if(f[x]==x) t=x;
Q.pop();
for(int i=0;i<E[x].size();i++)
{
if(!vis[E[x][i]])
{
Q.push(E[x][i]);
fa[E[x][i]]=x;
vis[E[x][i]]=1;
}
else if(fa[x]!=E[x][i]&&!cir)
{
if(num==-1) num=x;
t=x;
cir=1;//标记,免得环里的元素重复出现...
}
}
}
return t;//一个联通量里只会有一个根节点或者一个环
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif // LOCAL
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&f[i]);
if(f[i]==i) flag=1,num=i;
E[i].push_back(f[i]);
E[f[i]].push_back(i);
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i]=1;//忘了这句一直WA...MDZZ
int k=bfs(i);
//cout<<k<<endl;
circle[++cnt]=k;
}
}
printf("%d\n",cnt-flag);
for(int i=1;i<=cnt;i++)
{
f[circle[i]]=num;
}
for(int i=1;i<=n;i++)
{
printf("%d%c",f[i],i==n?'\n':' ');
}
return 0;
}
蒟蒻加油。