oneman233

2016-2017 ACM-ICPC Northwestern European Regional Programming Contest (NWERC 2016)

A、给你一堆十进制数字,允许前导零,大小比较的方式同字典序
你需要更改最少的数位,使得整个序列单调不增

究极神秘的dp:
\(dp[i+1][j+k]=cal(dp[i][j])(0<=k<=2*n)\)

其中:
\(dp[i][j]\)表示前\(i\)个数字更改了\(j\)次之后所能得到的最小“数字”
这意味着dp数组里储存的是一个字符串

接下来是窒息的\(cal()\)函数,用于计算\(k\)步之内能得到的最小字符串,并且还要满足\(>=dp[i][j]\)

cal函数实际上贪心地找即可,先看一下能不能把每一位都设置得跟\(dp[i][j]\)一样
不能的话就向前找到第一位不是\(9\)的,然后把这儿\(+1\)即可

最后别忘了剩下的所有数位置\(0\),因为要保证最小

dp的初值弄一个全0的合法串即可,还要记录一下转移路径方便输出

代码:

#include <bits/stdc++.h>
#define FAST ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
using namespace std;
const int N=45;

int n,m;
string s[N];
string dp[N][N*2];
int pre[N][N*2];
bool can[N][N*2];

bool gao(string &p,string s,string t,int k)
{
	p=s;
	int cnt=k,i=0;
	for(;i<m&&cnt>0;++i)
	{
		if(p[i]!=t[i])
		{
			p[i]=t[i];
			cnt--;
		}
	}
	if(p>=t) return 1;
	for(--i;i>=0&&p[i]=='9';--i);//
	if(i<0) return 0;
	p=s;
	cnt=k;
	for(int j=0;j<i;++j)
	{
		if(p[j]!=t[j])
		{
			p[j]=t[j];
			cnt--;
		}
	}
	if(p[i]!=t[i]+1)
	{
		p[i]=t[i]+1;
		cnt--;
	}
	for(int j=i+1;j<m&&cnt>0;++j)
	{
		p[j]='0';
		cnt--;
	}
	return 1;
}

void gao2()
{
	can[0][0]=1;
	for(int i=0;i<m;++i)
		dp[0][0]+='0';
}

int main()
{
	FAST
	cin>>n>>m;for(int i=1;i<=n;++i) cin>>s[i];
	gao2();
	for(int i=0;i+1<=n;++i)
	{
		for(int j=0;j<=2*n;++j)
		{
			if(!can[i][j]) continue;
			string p=s[i+1];
			for(int k=0;j+k<=2*n;++k)
			{
				if(gao(p,s[i+1],dp[i][j],k)&&(!can[i+1][j+k]||dp[i+1][j+k]>p))
				{
					can[i+1][j+k]=1;
					pre[i+1][j+k]=j;
					dp[i+1][j+k]=p;
				}
			}
		}
	}
	stack<string> ans;
	for(int j=0;j<=2*n;++j)
	{
		if(can[n][j])
		{
			for(int i=n;i>=1;--i)
			{
				ans.push(dp[i][j]);
				j=pre[i][j];
			}
			break;
		}
	}
	while(!ans.empty())
	{
		cout<<ans.top()<<endl;
		ans.pop();
	}
	return 0;
}

B、有向图最长链,本来是NP问题,但是有限定环的大小不超过5

那么就先缩点,然后SCC内暴力转移,再把dp值转移给相邻的SCC,拓扑排序即可

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int inf=0x3f3f3f3f;

int n,m,x[N*10],y[N*10],tmp[N*10];
vector<int> e[N],scc[N],e2[N];
int dfn[N],low[N],timer=0,c[N],color=0;
int deg[N],dp[N];
bool vis[N];
stack<int> s;

void add(int x,int y){e[x].push_back(y);}
void add2(int x,int y){e2[x].push_back(y);}

void tarjan(int u)
{
	dfn[u]=low[u]=++timer;
	s.push(u);
	vis[u]=1;
	for(auto v:e[u])
	{
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v]) low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u])
	{
		++color;
		int t;
		do
		{
			t=s.top();
			s.pop();
			c[t]=color;
			vis[t]=0;
			scc[color].push_back(t);
		}
		while(u!=t);
	}
}

void gao2(int x,int cnt)
{
	vis[x]=1;
	for(auto y:e[x])
	{
		if(vis[y]==0&&c[x]==c[y])//
			gao2(y,cnt+1),dp[y]=max(dp[y],cnt+1);
		else if(c[x]!=c[y])//
			dp[y]=max(dp[y],1+cnt);
	}
	vis[x]=0;
}

void gao()
{
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=m;++i)
	{
		if(c[x[i]]!=c[y[i]])
			add2(c[x[i]],c[y[i]]),deg[c[y[i]]]++;
	}
	queue<int> q;
	for(int i=1;i<=color;++i)
		if(deg[i]==0)
			q.push(i);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(auto i:scc[x])
			tmp[i]=dp[i];
		for(auto i:scc[x])
			gao2(i,tmp[i]);
		for(auto y:e2[x])
		{
			deg[y]--;
			if(deg[y]==0)
				q.push(y);
		}
	}
//	for(int i=1;i<=n;++i) printf("? %d ",dp[i]);
	int ans=-inf;
	for(int i=1;i<=n;++i) ans=max(ans,dp[i]);
	printf("%d",ans+1);
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&x[i],&y[i]);
		add(x[i],y[i]);
	}
	for(int i=1;i<=n;++i)
		if(!dfn[i])
			tarjan(i);
	gao();
	return 0;
}

C、签到,垂直方向和水平方向速度不变,在某些区域水平方向会暂时加速,问你初速度是多少才能恰好抵达某个点

实际上就是解一元一次方程

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
double x,y,f;
int n,l,r;
 
int main()
{
	scanf("%lf%lf",&x,&y);
	scanf("%d",&n);
	double sum=0.0,tmp=0.0;
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d%lf",&l,&r,&f);
		sum+=(r-l)*f;
		tmp+=r-l;
	}
	sum+=y-tmp;
	printf("%.10lf",x/sum);
	return 0;
}

E、签到,题意比较迷,老师会选一个房间开始发卷子,每个房间会发出这个房间里的人数张卷子,然后再收进来人数张卷子,问你一圈下来会不会有人收到自己的卷子

从人数最多的房间开始发,模拟一遍判断第一个房间的人有没有拿到自己卷子即可

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=35;
const int inf=0x3f3f3f3f;
 
int n;
pair<int,int> p[N];
vector<int> ans;
queue<int> q;
 
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&p[i].first);
		p[i].second=i;
	}
	sort(p+1,p+1+n);
	ans.push_back(p[n].second);
	for(int i=1;i<=p[n].first;++i)
		q.push(p[n].second);
	for(int i=n-1;i>=1;--i)
	{
		for(int j=1;j<=p[i].first;++j)
			q.pop();
		for(int j=1;j<=p[i].first;++j)
			q.push(p[i].second);
		ans.push_back(p[i].second);
	}
	bool can=1;
	int f;
	while(!q.empty())
	{
		f=q.front();
		if(f==p[n].second)
		{
			can=0;
			break;
		}
		q.pop();
	}
	if(can)
	{
		for(auto i:ans)	printf("%d ",i);
	}
	else
	puts("impossible");
	return 0;
}

F、队友写的二分

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int n,a[1000005],b[1000005];
 
bool check(int x)
{
	vector<int> A,B;
	for(int i=0;i<n;i++)
	{
		if(a[i]>x) A.push_back(a[i]);
		if(b[i]>x) B.push_back(b[i]);
	}
	if(A.size()%2||B.size()%2) return false;
	for(int i=0;i<A.size();i+=2)
		if(A[i]!=A[i+1]) return false;	
	for(int i=0;i<B.size();i+=2)
		if(B[i]!=B[i+1]) return false;
	return true;
}
 
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",a+i);
	for(int i=0;i<n;i++)
		scanf("%d",b+i);
	int l=0,r=1000000000;
	while(l<r)
	{
		int m=l+r>>1;
		if(check(m)) r=m; else l=m+1;	
	}
	printf("%d",l);
	return 0;
}

H、队友写的格雷码

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int n;
ll a[100],b[100],A,B;
 
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%1lld",&a[i]);
	for(int i=0;i<n;i++)
		scanf("%1lld",&b[i]);
	for(int i=1;i<n;i++)
		a[i]^=a[i-1],b[i]^=b[i-1];
	for(int i=0;i<n;i++)
	{
		A<<=1,B<<=1;
		A+=a[i],B+=b[i];
	}
	printf("%lld",max(0ll,abs(A-B)-1));
	return 0;
}

I、怪怪最短路,有两种矿井,每个点都可能是其中的一种,求从1开始到A矿井和B矿井经过的点数最少是多少

考虑跑三次dij,分别从1、A的超级源点、B的超级源点三个位置开始,求出1到所有点、所有A到所有点、所有B到所有点的最短路

注意A和B的超级源点要跑反向边,因为是有向图

然后枚举每个点即可,答案是三个距离之和的最小值

正确性有个感性的证明,答案点应该是一个“三岔口”,它经过的重复点数最少

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
 
int n,m,C,O,diso[100005],disc[100005],dis[100005],vis[100005],o[100005],c[100005];
struct edge
{
	int y,v;
	edge(int Y,int V):y(Y),v(V){}
};
vector<edge> e[100005],rev[100005];
 
inline void add(int x,int y,int v)
{
	e[x].emplace_back(y,v);
}
 
inline void revadd(int x,int y,int v)
{
	rev[x].emplace_back(y,v);
}
 
void dij(int dis[],vector<edge> e[],int s)
{
	//memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[s]=0;
	priority_queue<pii,vector<pii>,greater<pii>> q;
	q.emplace(0,s);
	while(!q.empty())
	{
		int x=q.top().second;q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(auto y:e[x])
			if(dis[x]+y.v<dis[y.y])
			{
				dis[y.y]=dis[x]+y.v;
				q.emplace(dis[y.y],y.y);
			}
	}
}
 
int main()
{
	scanf("%d%d%d",&n,&O,&C);
	for(int i=0,x;i<O;i++)
	{
		scanf("%d",&x);
		revadd(n+1,x,0);
	}
	for(int i=0,x;i<C;i++)
	{
		scanf("%d",&x);
		revadd(n+2,x,0);
	}
	for(int i=1,tot,x;i<=n;i++)
	{
		scanf("%d",&tot);
		for(int j=0;j<tot;j++)
			scanf("%d",&x),add(i,x,1),revadd(x,i,1);
	}
	memset(dis,0x3f,sizeof(dis));
	memset(disc,0x3f,sizeof(disc));
	memset(diso,0x3f,sizeof(diso));
	dij(diso,rev,n+1);
	dij(disc,rev,n+2);
	dij(dis,e,1);
	ll ans=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
		ans=min(ans,(long long)diso[i]+disc[i]+dis[i]);
	if(ans>=0x3f3f3f3f) printf("impossible"); else printf("%lld",ans);
	return 0;
}

J、队友说似乎是个大模拟?欧洲人的英语确实有点迷惑

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main()
{
	int n,q,s;
	ll sumd=0;
	ll suma=0;
	int tar[105]={0};
	ll qsize[35]={0};
	ll qleft[35]={0};
	ll buffer=0;
	
	scanf("%d %d %d",&n,&q,&s);
	for(int i=1;i<=s;i++)
	{
		scanf("%d",tar+i);
	}
	for(int i=1;i<=q;i++)
	{
		scanf("%lld",qsize+i);
		qleft[i]=qsize[i];
	}
	ll par,tmp;
	for(int i=0;i<n;i++)
	{
		scanf("%lld",&tmp);
		sumd+=tmp;
		ll qthis[35]={0};
		for(int j=1;j<=s;j++)
		{
			scanf("%lld",&par);
			qthis[tar[j]]+=par;
			suma+=par;
		}
		for(int j=1;j<=q;j++)
		{
			if(qthis[j]>qsize[j])
			{
				cout<<"impossible";
				return 0;
			}
		}
		for(int j=1;j<=q;j++)
		{
			qleft[j]-=qthis[j];
			if(qleft[j]<0)
			{
				buffer+=qleft[j];
				qleft[j]=0;
				if(buffer<0)
				{
					cout<<"impossible";
					return 0;
				}
			}
		}
		buffer+=tmp;
	}
	if(sumd>=suma)
	{
		cout<<"possible";
	}
	else
	{
		cout<<"impossible";
	}
	
	return 0;
}

posted on 2019-10-31 15:18  oneman233  阅读(126)  评论(0编辑  收藏  举报

导航