CF637(1341,1340)

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. 起点为1,起始时间为0,最终回到1
  2. 从<vi,ti>到<vi+1,ti+1>有如下两种转移方式
    a) 向相邻节点走,时间+1,即vi与vi+1相邻,ti+1=ti+1
    b) 时间回溯,即vi=vi+1,ti>ti+1>=0
  3. <vi,ti>只出现过一次
  4. ti的最大值最小

题解

  1. 每一个点x至少要经过degree[x]次(degree[x]表示x的度),因而所有点经过次数的最大值的最小值为max(degree[x])
  2. 当由父节点x向子节点y进行dfs时,考虑给每个点出去的边分配一个区间 t+1,t+2,t+3……(t为达到该点的时间),当t>max(degree[x])时,使用时间回溯,t-=son[x]+1
  3. 当从y子节点返回x时,先使用时间回溯,使得时间回到到达y节点的时间-1,然后回到x节点,并且时间+1
  4. 这样,从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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值