Day2 尤拉路,拓撲排序和差分約束

~hsm~發表於2019-02-12

A.BZOJ 3033: 太鼓達人

題目

BZOJ 3033

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+50;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int n,t,ans[maxn];
bool used[maxn];
inline bool eular(int x,int k)//x是當前子串的十進位制表示,k是當前查詢次
{
	if (used[x]) return 0;
	used[x]=1,ans[k]=x&1;//統計答案,&的規則是:有零則零,否則為一,返回末位數字
	if (k==t) return 1;//查詢結束
	if (eular((x<<1)&(t-1),k+1)) return 1;
	if (eular((x<<1|1)&(t-1),k+1)) return 1;//將每個二進位制數看成一個點,將他前面的數刪去,並在它後面加上0/1就能得到兩個新數
	used[x]=0;//回溯,以免影響之後的dfs
	return 0;
}
int main()
{
	read(n);
	t=1<<n;
	printf("%d ",t);
	eular(t-2,1);//從每位走n位子串,可得到的完整子串數量為2^n-2
	for (int i=1;i<=t;++i)
		printf("%d",ans[i]);
	return 0;
}

邱神現場A題:

#include<bits/stdc++.h> 
using namespace std;
int read()
{
	int a;
	cin>>a;
	return a;
}
int i,k,m;
struct node
{
	int x,deep;
}o[10010];
bool Orz(node a,node b)
{
	return a.deep<b.deep;
}
void dfs(int now,int deep)
{
	if(deep==m+1)
	{
		now=now%(m/2);
		now=now*2;
		if(now)
			return ;
		sort(o,o+m,Orz);
		for(i=1;i<k;i++)
			cout<<0;
		for(i=0;i<=m-k;i++)
			cout<<o[i].x%2;
		
		exit(0) ;
	}
	//這裡應該有一個關於deep和now的if使得dfs停下來並輸出
	now=now%(m/2);
	now=now*2;
	if(!o[now].deep)
	{	
		o[now].deep=deep;
		dfs(now,deep+1);
		o[now].deep=0;
	}
	now++;
	if(!o[now].deep)
	{
		o[now].deep=deep;
		dfs(now,deep+1);
		o[now].deep=0;
	}
	return ;
}
int main()
{	
	//freopen("123.in","r",stdin);
	k=read();m=int(pow(2,k*1.0));
	cout<<m<<' ';
	for(i=0;i<m;i++)
		o[i].x=i;
	o[0].deep=1;
	dfs(0,2);
}

B. POJ 1386 Play on Words

題目

POJ 1386

題解

其實就是讓判斷是否是尤拉回路或尤拉通路.
建圖需要一點思維,把26個字母當成是節點,每個單詞當成是一條有向邊。
比如單詞acmacm就是一條aamm的有向邊。
然後用並查集判斷圖是否聯通即可。

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=30;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int fa[maxn];
inline int get(int x)
{
	if (x==fa[x]) return x;
	return fa[x]=get(fa[x]);
}
int indeg[maxn],outdeg[maxn];
int main()
{
	int t;read(t);
	while (t--)
	{
		int n;read(n);
		memset(indeg,0,sizeof(indeg));
		memset(outdeg,0,sizeof(outdeg));
		for (int i=0;i<26;++i)
			fa[i]=i;
		char str[1005];
		for (int i=1;i<=n;++i)
		{
			scanf("%s",str);
			int x=str[0]-'a';
			int y=str[strlen(str)-1]-'a';
			if (get(x)!=get(y))
				fa[get(x)]=get(y);
			++indeg[y],++outdeg[x];
		}
		bool flag=true;
		int cnt=0,In=0,Out=0;
		for (int i=0;i<26;++i)
		{
			if (get(i)==i && (indeg[i]+outdeg[i])>0)
				++cnt;
			if (indeg[i]!=outdeg[i])
			{
				if (indeg[i]-1==outdeg[i])
					++In;
				else if (indeg[i]+1==outdeg[i])
					++Out;
				else
					flag=false;
			}
		}
		if (flag && cnt==1 && (In==1 && Out==1 || In==0 && Out==0))
			puts("Ordering is possible.");//尤拉回路和通路都符合題目條件
		else
			puts("The door cannot be opened.");
	}
	return 0;
}

C. CF 516B. Drazil and Tiles

題目

CF 516B

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+2;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
const char ch[]="><v^";
const char str[]="<>^v";
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct rec
{
	int x,y;
}a[maxn*maxn];
int m,n,tot,G[maxn][maxn];
char c[maxn][maxn];
inline bool check(int x,int y)
{
	return x>0 && x<=m && y>0 && y<=n;
}
inline void dfs(int x,int y)
{
	int cnt=0;
	for (int i=0;i<4;++i)
	{
		int xx=x+dx[i],yy=y+dy[i];
		if (check(xx,yy) && c[xx][yy]=='.') ++cnt;
	}
	G[x][y]=cnt;
}
inline bool topsort()
{
	queue<pair<int,int> >q;
	int cnt=0;
	for (int i=0;i<tot;++i)
	{
		int x=a[i].x,y=a[i].y;
		if (G[x][y]==1)
		{
			q.push(pair<int,int> (x,y));
			G[x][y]=0;
		}
	}
	pair<int,int>m;
	while (!q.empty())
	{
		m=q.front();
		q.pop();
		int x=m.first,y=m.second;
		--G[x][y];
		for (int i=0;i<4;++i)
		{
			int xx=x+dx[i],yy=y+dy[i];
			if (check(xx,yy) && c[xx][yy]=='.')
			{
				G[xx][yy]=0;
				c[xx][yy]=ch[i];
				c[x][y]=str[i];
				for (int j=0;j<4;++j)
				{
					int xxx=xx+dx[j],yyy=yy+dy[j];
					if (check(xxx,yyy) && c[xxx][yyy]=='.')
					{
						dfs(xxx,yyy);
						if (G[xxx][yyy]==1)
							q.push(pair<int,int> (xxx,yyy));
					}
				}
				cnt+=2;
			}
		}
	}
	return cnt==tot;
}
int main()
{
	read(m);read(n);tot=0;
	for (int i=1;i<=m;++i)
	{
		scanf("%s",c[i]+1);
		for (int j=1;j<=n;++j)
			if (c[i][j]=='.')
			{
				a[tot].x=i,a[tot++].y=j;
				G[i][j]=0;
				if (check(i-1,j) && c[i-1][j]=='.')
					++G[i-1][j],++G[i][j];
				if (check(i,j-1) && c[i][j-1]=='.')
					++G[i][j-1],++G[i][j];
			}
	}
	if (topsort())
		for (int i=1;i<=m;++i)
			printf("%s\n",c[i]+1);
	else
		puts("Not unique");
	return 0;
}

D. POJ 3169 Layout

題目

POJ 3169

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=1e6+10;
const int inf=0x3f3f3f3f;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn*2],edge[maxn*2],Next[maxn*2],head[maxn],len=0;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
int dist[maxn],cnt[maxn],vis[maxn];
inline int spfa(int s,int n)
{
	for (int i=0;i<maxn;++i) dist[i]=inf;
	memset(cnt,0,sizeof(cnt));
	memset(vis,0,sizeof(vis));
	queue<int>q;
	dist[s]=0,vis[s]=1;
	q.push(s),++cnt[s];
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		vis[x]=0;
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i],z=edge[i];
			if (dist[y]>dist[x]+z)
			{
				dist[y]=dist[x]+z;
				if (!vis[y])
				{
					vis[y]=1,q.push(y);
					if (++cnt[y]>n) return -1;//存在負環
				}
			}
		}
	}
	if (dist[n]==inf) return -2;
	return dist[n];
}
int main()
{
	int n,x,y;
	read(n);read(x);read(y);
	for (int i=1;i<n;++i)
		add(i+1,i,0);
	for (int i=1;i<=x;++i)
	{
		int a,b,d;
		read(a);read(b);read(d);
		add(a,b,d);
	}
	for (int i=1;i<=y;++i)
	{
		int a,b,d;
		read(a);read(b);read(d);
		add(b,a,-d);
	}
	printf("%d\n",spfa(1,n));
	return 0;
}

E. POJ 2983 Is the Information Reliable?

題目

POJ 2983

題解

nm題意:有n個點,給出m條資訊,
PABXVAB1若為P則表示點A在點B的北方X米,若為V則表示A在B的北方未知距離,但大於1。
判斷給出的資訊是否合法。

思路:對於不等式建立邊的條件,
Pab=xab&gt;=xab&lt;=xba&gt;=xP:a-b=x \rightarrow a-b&gt;=x,a-b&lt;=x \rightarrow b-a&gt;=-x
Vab&gt;=1V:a-b&gt;=1
spfa0然後跑一遍spfa即可,記住起點為0號節點。
0因為我們建立了一個0號節點作為超級源點。

程式碼

#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
typedef long long ll;
const int maxm=1e5+10,maxn=1010;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxm<<1],Next[maxm<<1],head[maxn],len;
ll edge[maxm<<1];
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
ll dist[maxn];
int n,m,vis[maxn],cnt[maxn];
inline bool spfa(int s)
{
	memset(dist,0x3f,sizeof(dist));
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	queue<int>q;
	dist[s]=0,vis[s]=1;
	q.push(s);
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		vis[x]=0;
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i],z=edge[i];
			if (dist[y]>dist[x]+z)
			{
				dist[y]=dist[x]+z;
				if (!vis[y])
				{
					q.push(y),vis[y]=1;
					if (++cnt[y]>n) return false;
				}
			}
		}
	}
	return true;
}
int main()
{
	while (~scanf("%d %d",&n,&m))
	{
		memset(head,0,sizeof(head));
		len=0;
		char s[5];int x,y;ll z;
		for (int i=1;i<=m;++i)
		{
        	scanf("%s",s);
			if (s[0]=='P')
			{
				read(x);read(y);read(z);
				add(x,y,-z);add(y,x,z);
			}
			else
			{
				read(x);read(y);
				add(x,y,-1);//x1-x2>=1,轉化為約束條件為x2-x1<=-1
			}
		}
		for (int i=1;i<=n;++i)
			add(0,i,0);//新增源點到其他頂點的邊,權值為0
		if (!spfa(0))
			puts("Unreliable");
		else
			puts("Reliable");
	}
	return 0;
}

F. HDU 3440 House Man

題目

HDU 3440

題解

1.兩個相鄰點之間最小距離為一,則有d[i+1]d[i]&gt;=1d[i+1]-d[i]&gt;=1,等價於d[i]d[i+1]&lt;=1d[i]-d[i+1]&lt;=-1;
2.高度最接近的兩個點的最大距離為m,按距離排序之後可有d[j+1]d[j]&lt;=md[j+1]-d[j]&lt;=m;
這樣,差分約束系統的條件找出來了;

剩下就是構圖的事了。
由的d[i]d[i+1]&lt;=1d[i]-d[i+1]&lt;=-1的編輯條件可知,我們給各個點制定了一個方向,就是從左到右是依次增大的。
這就限定了的d[j+1]d[j]&lt;=md[j+1]-d[j]&lt;=m的連邊條件,必須是由對應的序號小的點連向序號大的點,否則,就把他們swapswap一下就好了。

程式碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
struct orz
{
	int id,height;
}a[maxn];
int ver[maxn<<4],edge[maxn<<4],Next[maxn<<4],head[maxn],len;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
inline bool cmp(orz a,orz b)
{
	return a.height<b.height;
}
int dist[maxn],vis[maxn],cnt[maxn];
inline int spfa(int s,int e,int n)
{
	memset(dist,0x3f,sizeof(dist));
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	queue<int>q;
	dist[s]=0,vis[s]=1;
	q.push(s);
	while (!q.empty())
	{
		int x=q.front();
		q.pop();
		vis[x]=0;
		if (++cnt[x]>n) return -1;
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i],z=edge[i];
			if (dist[y]>dist[x]+z)
			{
				dist[y]=dist[x]+z;
				if (!vis[y]) q.push(y),vis[y]=1;
			}
		}
	}
	return dist[e];
}
int main()
{
	int t;read(t);
	for (int cas=1;cas<=t;++cas)
	{
		memset(head,0,sizeof(head));//初始化
		len=0;
		int n,D;
		read(n);read(D);
		for (int i=1;i<=n;++i)
		{
			read(a[i].height),a[i].id=i;
			if (i!=n)
				add(i+1,i,-1);
		}
		sort(a+1,a+n+1,cmp);
		for (int i=1;i<n;++i)
		{
			int x=a[i].id,y=a[i+1].id;
			if (x>y)
				swap(x,y);
			add(x,y,D);
		}
		int s=a[1].id,e=a[n].id;
		if (s>e)
			swap(s,e);
		printf("Case %d: %d\n",cas,spfa(s,e,n));
	}
	return 0;
}

G. POJ 1275 Cashier Employment

題目

POJ 1275

題解

num[i]num[i] 為來應聘的在第i個小時開始工作的人數

r[i]r[i] 為第i個小時至少需要的人數

x[i]x[i] 為招到的在第i個小時開始工作的人數

根據題意有:

0&lt;=x[i]&lt;=num[i]0 &lt;= x[i] &lt;= num[i]

x[i]+x[i1]++x[i7]&gt;=r[i](8)x[i] + x[i-1] + …+ x[i-7] &gt;= r[i] (題目中的連續工作8小時)

再設s[i]=x[1]++x[i]s[i] = x[1] + … + x[i]

則有:s[i]s[i1]&gt;=0s[i] – s[i-1] &gt;= 0

s[i1]s[i]&gt;=num[i]s[i-1] – s[i] &gt;= –num[i]

s[i]s[i8]&gt;=r[i],8&lt;=i&lt;=24s[i] – s[i-8] &gt;= r[i], 8 &lt;= i &lt;= 24

s[i]s[i+16]&gt;=r[i]s[24],1&lt;=i&lt;=7s[i] – s[i+16] &gt;= r[i] – s[24], 1 &lt;= i &lt;= 7

還需要新增一個隱藏不等式: s[24]s[0]&gt;=anss[24] – s[0] &gt;= ans(列舉的答案)

通過列舉s[24]s[24],來檢測是否滿足條件,題目是求最小值,即求最長路,以0為源點。

最後,其實是不用寫二分的,寫一個手寫佇列,跑的比二分都快!

程式碼

/***************************************************************************
	Result		Memory	Time	Language	Code Length	Submit Time
	Accepted	100K	16MS	C++ 		2219B 		2019-02-26 21:33:34
****************************************************************************/
#include<algorithm>
#include<bitset>
#include<cctype>
#include<cerrno>
#include<clocale>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<limits>
#include<list>
#include<map>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<utility>
#include<vector>
#include<cwchar>
#include<cwctype>
using namespace std;
const int maxn=30,inf=0x7fffffff;
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1,ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}
int ver[maxn*30],edge[maxn*30],Next[maxn*30],head[maxn],len;
inline void add(int x,int y,int z)
{
	ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}
int dist[maxn],cnt[maxn],q[maxn*30];
bool vis[maxn];
inline bool spfa(int ans)
{
	int hd=0,tl=1;//即head,tail
	for (int i=0;i<=24;++i)
		dist[i]=-inf,vis[i]=cnt[i]=0;
	dist[0]=0,vis[0]=1;
	q[0]=0;//手寫佇列
	while (hd<tl)
	{
		int x=q[hd];
		++hd;
		vis[x]=0;
		for (int i=head[x];i;i=Next[i])
		{
			int y=ver[i],z=edge[i];
			if (dist[y]<dist[x]+z)
			{
				dist[y]=dist[x]+z;
				if (!vis[y]) q[tl]=y,++tl,vis[y]=1;
				if (++cnt[y]>24) return 0;
			}
		}
	}
	if (dist[24]==ans)
		return 1;
	return 0;
}
int num[maxn];//num[i]標示i時刻開始工作的人
int r[maxn];//r[i]標示i時刻至少需要多少人
inline void build(int ans)//建圖這也是本題目的關鍵,建模
{
	for (int i=0;i<=24;++i)
		head[i]=0;
	len=0;
	add(0,24,ans);
	for (int i=1;i<=24;++i)
		add(i-1,i,0),add(i,i-1,-num[i]);
	for (int i=1;i<=8;++i)
		add(i+16,i,r[i]-ans);
	for (int i=9;i<=24;++i)
		add(i-8,i,r[i]);
}
int main()
{
	int t;read(t);
	while (t--)
	{
		bool flag=0;
		for (int i=1;i<=24;++i)
			read(r[i]),num[i]=0;
		int n;read(n);
		for (int i=1;i<=n;++i)
		{
			int tim;read(tim);
			++num[tim+1];
		}
		for (int i=0;i<=n;++i)
		{
			build(i);
			if (spfa(i))
			{
				flag=1;
				printf("%d\n",i);
				break;
			}
		}
		if (!flag)
			puts("No Solution");
	}
	return 0;
}

相關文章