div 2 D
Nastya and Scoreboard
题意:用数码管显示数字。先给定n个数码管显示情况,问再点亮恰好k个二极管,可以显示的最大的数字。
题解:
(1)dist[i][j]表示每i个数码管,将其变成数字j所需点亮二极管的数量
(2)dp[i][j]表示对于后i个数码管,点亮j个二极管,是否能组成一组数字(dp[i][j]=true or false)
(3)枚举每一位数码管i,贪心将其改成数字j(从9到0枚举j),看剩下的二极管数量是否能将[i+1,n]区间修改成合法状态。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int inf=1e9+7;
const int N=2010;
int dist[N][10],dp[N][N],digit[10]={119,36,93,109,46,107,123,37,127,111};
int get_dist(int x,int y)
{
int cnt=0,i;
for(i=0;i<7;i++)
{
if((x&(1<<i))&&!(y&(1<<i)))
return inf;
if(!(x&(1<<i))&&(y&(1<<i)))
cnt++;
}
return cnt;
}
int main()
{
int n,i,j,k,num,ii;
char s[10];
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)
{
scanf("%s",s);
num=0;
for(j=6;j>=0;j--)
num=num*2+s[j]-'0';
for(j=0;j<=9;j++)
dist[i][j]=get_dist(num,digit[j]);
}
dp[n+1][0]=1;
for(i=n;i>0;i--)
{
for(j=k;j>=0;j--)
{
for(ii=0;ii<=9;ii++)
{
if(j>=dist[i][ii]&&dp[i+1][j-dist[i][ii]])
dp[i][j]=1;
}
}
}
if(dp[1][k]==0)
{
printf("-1");
return 0;
}
for(i=1;i<=n;i++)
{
for(j=9;j>=0;j--)
{
if(k>=dist[i][j]&&dp[i+1][k-dist[i][j]])
{
printf("%d",j);
k-=dist[i][j];
break;
}
}
}
return 0;
}
div 2 E
Nastya and Unexpected Guest
题意:红绿灯游戏,绿灯g秒,红灯r秒。由0点出发,目的地为n点,中间有m个安全岛。当红灯亮起时,必须停留在安全岛上;当绿灯亮起时必须一直沿某一方向走。只有在安全岛上的时候才可以改变行进方向。求到达n点所用最少时间,不可达到输出-1。
一般来说,这类题目有两种方向:
1.状态无穷,使用某些数学方法求解
2.所有状态可枚举,用bfs或者dp等方法求解
这道题属于第二种情况。
题解:
在ti时间可以到达x点,那么对于tj时间(ti<tj&&ti%g==tJ%g),那么ti必然比tj更优。
只需用<位置x,时间t(0<=t<g)>二元组即可描述所有状态。
双端队列bfs
对于状态<x,t>,枚举x-1和x+1的情况。
对于x-1,
若t+distx - distx-1<g,在队首插入状态<x-1,t+distx - distx-1>,距离不变
若t+distx - distx-1=g,在队尾插入状态<x-1,0>,距离加上(g+r)
若t+distx - distx-1>g,舍弃。
对于x+1的情况同理。
统计答案,vist[x][t]表示到达<x,t>状态最少走过的整段(g+r),ans=min(vist[m][i]+i),注意特判i=0的情况和无解的情况。
P.S. 一组数据
input
5 6
0 1 2 3 4 5
1 1
output
9
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e4+10;
const int M=1e3+10;
struct NODE{
int pos,tim;
NODE(){}
NODE(int x,int y)
{
pos=x;
tim=y;
}
}x;
deque<NODE>que;
long long vist[N][M];
int a[N];
int n,m,g,r;
void bfs()
{
memset(vist,255,sizeof(vist));
que.push_back(NODE(1,0));
vist[1][0]=0;
while(!que.empty())
{
x=que.front();
que.pop_front();
if(x.pos>1&&x.tim+a[x.pos]-a[x.pos-1]<=g)
{
if(vist[x.pos-1][(x.tim+a[x.pos]-a[x.pos-1])%g]==-1)
{
if(x.tim+a[x.pos]-a[x.pos-1]==g)
que.push_back(NODE(x.pos-1,(x.tim+a[x.pos]-a[x.pos-1])%g));
else
que.push_front(NODE(x.pos-1,(x.tim+a[x.pos]-a[x.pos-1])%g));
vist[x.pos-1][(x.tim+a[x.pos]-a[x.pos-1])%g]=vist[x.pos][x.tim]+(x.tim+a[x.pos]-a[x.pos-1])/g*(g+r);
}
else
vist[x.pos-1][(x.tim+a[x.pos]-a[x.pos-1])%g]=min(vist[x.pos-1][(x.tim+a[x.pos]-a[x.pos-1])%g],vist[x.pos][x.tim]+(x.tim+a[x.pos]-a[x.pos-1])/g*(g+r));
}
if(x.pos<m&&x.tim+a[x.pos+1]-a[x.pos]<=g)
{
if(vist[x.pos+1][(x.tim+a[x.pos+1]-a[x.pos])%g]==-1)
{
if(x.tim+a[x.pos+1]-a[x.pos]==g)
que.push_back(NODE(x.pos+1,(x.tim+a[x.pos+1]-a[x.pos])%g));
else
que.push_front(NODE(x.pos+1,(x.tim+a[x.pos+1]-a[x.pos])%g));
vist[x.pos+1][(x.tim+a[x.pos+1]-a[x.pos])%g]=vist[x.pos][x.tim]+(x.tim+a[x.pos+1]-a[x.pos])/g*(g+r);
}
else
vist[x.pos+1][(x.tim+a[x.pos+1]-a[x.pos])%g]=min(vist[x.pos+1][(x.tim+a[x.pos+1]-a[x.pos])%g],vist[x.pos][x.tim]+(x.tim+a[x.pos+1]-a[x.pos])/g*(g+r));
}
}
}
int main()
{
int i,mina=1000010,maxa=0;
long long ans=-1;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d",&a[i]);
mina=min(mina,a[i]);
maxa=max(maxa,a[i]);
}
scanf("%d%d",&g,&r);
if(mina>0)
a[++m]=0;
if(maxa<n)
a[++m]=n;
sort(a+1,a+m+1);
bfs();
for(i=0;i<=g;i++)
{
if(ans==-1&&vist[m][i]!=-1)
ans=vist[m][i]+i;
else
if(ans!=-1&&vist[m][i]!=-1)
ans=min(ans,vist[m][i]+i);
}
if(vist[m][0]>r&&(ans==-1||ans>vist[m][0]-r))//注意这个特判
ans=vist[m][0]-r;
printf("%lld",ans);
return 0;
}
div 2 F
Nastya and Time Machine
题意:给一棵树,构造一条路径,满足如下规则:
- 起点为1,起始时间为0,最终回到1
- 从<vi,ti>到<vi+1,ti+1>有如下两种转移方式
a) 向相邻节点走,时间+1,即vi与vi+1相邻,ti+1=ti+1
b) 时间回溯,即vi=vi+1,ti>ti+1>=0 - <vi,ti>只出现过一次
- ti的最大值最小
题解:
- 每一个点x至少要经过degree[x]次(degree[x]表示x的度),因而所有点经过次数的最大值的最小值为max(degree[x])
- 当由父节点x向子节点y进行dfs时,考虑给每个点出去的边分配一个区间 t+1,t+2,t+3……(t为达到该点的时间),当t>max(degree[x])时,使用时间回溯,t-=son[x]+1
- 当从y子节点返回x时,先使用时间回溯,使得时间回到到达y节点的时间-1,然后回到x节点,并且时间+1
- 这样,从x到y节点时间与y节点返回x节点时间相同,即<y,t>与<x,t>,因此不会影响遍历x的下一个儿子节点。同时t保持在[max_degree-son[x]-1 , max_degree]之间,必然不重复,且不超过max_degree。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N=1e5+10;
struct NODE{
int x,t;
NODE(){}
NODE(int a,int b)
{
x=a;
t=b;
}
};
vector<NODE>ans;
struct EDGE{
int to,nxt;
}edge[N*2];
int t[N],size[N];
int k,maxa;
void addedge(int x,int y)
{
edge[++k].to=y;
edge[k].nxt=t[x];
size[x]++;
t[x]=k;
}
void dfs(int x,int fa,int tim)
{
int tmp,p,y;
tmp=tim;
ans.push_back(NODE(x,tim));
p=t[x];
while(p)
{
y=edge[p].to;
if(y!=fa)
{
if(tim==maxa)
{
tim=maxa-size[x]-1;
if(x!=1)
tim++;
ans.push_back(NODE(x,tim));
}
tim++;
dfs(y,x,tim);
ans.push_back(NODE(x,tim));
}
p=edge[p].nxt;
}
if(x!=1&&tim!=tmp-1)
ans.push_back(NODE(x,tmp-1));
}
int main()
{
int n,i,x,y;
scanf("%d",&n);
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
for(i=1;i<=n;i++)
maxa=max(maxa,size[i]);
dfs(1,0,0);
printf("%d\n",ans.size());
for(i=0;i<ans.size();i++)
printf("%d %d\n",ans[i].x,ans[i].t);
return 0;
}