SXYZ-6.27专题比赛

好的,现在正式定义今天的比赛为一场伤心的比赛。


image

↑这张图片首先能说明一些问题,但这并不是关键。


↓这才是伤心的关键

image

↑第一题文件输入输入

image

↑第二题文件名直接

评语,一个比一个离谱!


然后只是很简单的把这两个小错误改正确以后就有83分了,不知道该喜还是该悲

上图

image

Ps:这是第一次犯这种SB错误,主要是平时自己就很小心。但依旧:

吃一堑,长一智

好的,就在刚刚,一脸雾水听完题目分析出来,又发现一个惊人事实,那就是我T2的文件输入输出名都是错的!!!!image
(狂喜)

T1 graph


首先考虑暴力做法+优化,拿了54分,思路是遍历每一个点,一每一个点为起点, dfs 深搜 K 个节点,跟 ans 取个最大值,最优解一定在这其中。

54分代码:

#include<bits/stdc++.h>
using namespace std;//听我说谢谢你,因为有你。。。。 
#define N 20000
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int nex[N],first[N],w[N],to[N],tot=0,vis[N];
int n,m,k,ans;
void add(int x,int y,int z){
	nex[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
void dfs(int u,int fa,int st,int sum){
	if(st==k){
		ans=max(ans,sum);	
		return ;
	} 
	++st;
	for(int e=first[u];e;e=nex[e]){
		int v=to[e];
		if(v!=fa&&vis[v]==0){
			vis[v]=1;
			sum+=w[e];
//			printf("u%d,v%d,w%d,st%d,sum%d\n",u,v,w[e],st,sum);
			dfs(v,u,st,sum);
			vis[v]=0;
			sum-=w[e];
		}
	}
}
int main(){
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	int T;
	T=read();
	while(T--){
		n=read();
		m=read();
		k=read();
		tot=0;
		ans=0;
		memset(first,0,sizeof(first));
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=m;i++){
			int a,b,c;
			a=read();b=read();c=read();
			add(a,b,c);
			add(b,a,c);
		}
		for(int i=1;i<=n;i++){
			dfs(i,0,1,0);
		}
		if(ans!=0)	printf("%d\n",ans);
		else printf("-1\n");
 	}
	return 0; 
}

思考正解做法,

color coding 对图中的点随机染色,色彩个数为 k,然后进行 dp,设 \(f[i][S]\) 为当前 i 点颜色合集为 S 的权值。

令所有 \(f[i][S] = −inf\),然后对于每个顶点 x 和其颜色 col[x],f[1 << col[x]][x] = 0。然后从小到大枚举状态 S,

每一个状态 S 下枚举边,对于每条边我们记其相邻 2 端为 xx, yy,边权为 zz。

\[f[xx][S] = max(f[xx][S], f[yy][S xor (1 << col[xx])] + zz) \]

\[f[yy][S] = max(f[yy][S], f[xx][S xor (1 << col[yy])] + zz) \]

重复执行足够多次以后取最优解。

#include<bits/stdc++.h>
const int N=1e4+5,B=500;
using namespace std;	
mt19937 rnd(19260817);
int n,m,k,col[N],S;
struct edge{int x,y,z;}e[N];
int f[N][64];
int check()
{
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<S;j++)f[i][j]=-2e9;
		f[i][1<<col[i]]=0;
	}
	for(int j=0;j<S;j++)
	for(int i=1,xx,yy,zz;i<=m;i++)
	{
		xx=e[i].x;yy=e[i].y;zz=e[i].z;
		if((j>>col[xx])&1)f[xx][j]=max(f[xx][j],f[yy][j^(1<<col[xx])]+zz);
		if((j>>col[yy])&1)f[yy][j]=max(f[yy][j],f[xx][j^(1<<col[yy])]+zz);
	}
	int res=-1;
	for(int i=1;i<=n;i++)res=max(res,f[i][S-1]);
	return res;
}
int ans;
int T;
int main()
{
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout); 
	ios::sync_with_stdio(false);cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m>>k;S=1<<k;
		for(int i=1;i<=m;i++)cin>>e[i].x>>e[i].y>>e[i].z;
		ans=-1;
		for(int i=1;i<=B;i++){
			for(int j=1;j<=n;j++)col[j]=rnd()%k;
			ans=max(ans,check());
		}
		cout<<ans<<'\n';
	}
	return 0; 
}

T2 random

随机
这道题考场没写出暴力啥,暴力都没写出来,主要是图的数据结构只会邻接表,不会以边存图。

来看一看全场做高分80分陈鑫的做法:模拟退货,而且思想异常的朴素,这就是高手与我的区别吗,随机将点排个序,然后遍历一遍,求前第 i 个做删点处理的代价,多做几次求 min。

80分大佬代码:

#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
using ll = long long;
mt19937 mt(random_device{}());
int t, n, m;
ll a, b;
ll ans;
pair <int, int> ed[45];
vector <int> gra[45];
bool mp[45][45];
bool vis[45];
int bs[45];
ll js() {
	ll ans = a * m;
	for (int j = 0; j <= n; ++j) {
		memset(vis, 0, sizeof(vis));
		int totp = j;
		for (int i = 1; i <= j; ++i) {
			bool flag = 0;
			for (auto &k : gra[bs[i]]) {
				if (!vis[k]) flag = 1;
				vis[k] = 1;
			}
			if (!flag) --totp;
		}
		int tot = 0;
		for (int k = 0; k < m; ++k) if (vis[k]) ++tot;
		ans = min(ans, b * totp + a * (m - tot));
	}
	return ans;
}
void mnth() {
	double T = 100000, dt = 0.99;
	while (T > 1e-8) {
		int x = mt() % (n - 1) + 1, y = mt() % (n - x) + x + 1;
		swap(bs[x], bs[y]);
		ll tmp = js();
		if (tmp < ans) {
			ans = tmp;
		} else if (exp(- abs(ans - tmp) / T) * 6553600 < mt() % 6553600) {
			swap(bs[x], bs[y]);
		}
		T *= dt;
	}
}
int main() {
	freopen("random.in", "r", stdin);
	freopen("random.out", "w", stdout);
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d%lld%lld", &n, &m, &a, &b);
		for (int i = 1; i <= n; ++i) gra[i].clear();
		memset(mp, 0, sizeof(mp));
		memset(bs, 0, sizeof(bs));
		ans = 1e18;
		for (int i = 0; i < m; ++i) {
			scanf ("%d%d", &ed[i].first, &ed[i].second);
			gra[ed[i].first].eb(i);
			gra[ed[i].second].eb(i);
		}
		for (int i = 1; i <= n; ++i) bs[i] = i;
		srand(time(NULL));
		random_shuffle(bs + 1, bs + n + 1);
		mnth();mnth();mnth();
		mnth();mnth();mnth();
		mnth();mnth();mnth();
		printf("%lld\n", ans);
	}
	return 0;
}

T3 substring

子串Ⅰ
考场上做了个简单判断,按照二的幂次分别输出0和1,然后随机情况输出0和1,竟然骗 了29.没想到这道题正解竟然是根据特殊性质做的,叫个什么 De Bruijn 序列。跑了,跑了。

不过部分分是可以拿de:

M.A.Martin 于 1934 年证明了以下贪心算法对所有的 n ≥ 2 都可以构造出一个长度

2n 的 De Bruijn 序列:

  1. 写出 n 个 0。

  2. 如果在序列尾部添加一个 1 后,和前面相连构成已经出现过的长为 n 的 01 子串,

则在序列尾部添加一个 0,否则在序列尾部添加一个 1。

  1. 序列若还不够 2n 项,则返回步骤 2。否则序列就是一个长度为 2n 次的 De Bruijn

序列。

posted @ 2023-06-27 12:57  alloverzyt  阅读(46)  评论(0编辑  收藏  举报