//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.7.26 结题报告

2023.7.26 DAY3

T1

搜索题,考试的时候没想出来正解,所以乱搞了一下,把没有匹配上的点给取出来然后枚举取最小值,30pts。

#include<bits/stdc++.h>

#define int long long
#define N 20

using namespace std;

int ans, X1[N], Y1[N], top1, X2[N], Y2[N], top2, vis[N];
char mp1[N][N], mp2[N][N];

signed main()
{
	for(int i = 1; i <= 4; i ++) 
		for(int j = 1; j <= 4; j ++)
			cin >> mp1[i][j];
	for(int i = 1; i <= 4; i ++)
	{
		for(int j = 1; j <= 4; j ++)
		{
			cin >> mp2[i][j];
			if(mp1[i][j] == '1' && mp2[i][j] == '1') mp1[i][j] = mp2[i][j] = '0';
			if(mp1[i][j] == '0' && mp2[i][j] == '1') X2[++ top2] = i, Y2[top2] = j;
			if(mp1[i][j] == '1' && mp2[i][j] == '0') X1[++ top1] = i, Y1[top1] = j;
		}
	}
//	cout << X2[1] << " " << Y2[1] << endl;
	for(int i = top1; i >= 1; i --)
	{
		int k = 0, minn = 1e9;
		for(int j = top2; j >= 1; j --)
		{
			if(vis[j]) continue;
			int res = abs(X1[i] - X2[j]) + abs(Y1[i] - Y2[j]);
			if(res < minn) k = j, minn = res;
		}
		ans += minn;
		vis[k] = 1;
	}
	cout << ans << endl; 
//	cout << top1 << "   " << top2 << endl;
	return 0;
}
/*
0001
0000
0010
0100
0010
1000
0001
0000
5
*/

期间我也打了一个广搜的代码,但是打挂了,我以为暴力不是正解,所以开始想别的,

#include <bits/stdc++.h>

#define int long long
#define N 11

using namespace std;

int dx[] = {0, 0, 0, 1, -1,};
int dy[] = {0, 1, -1, 0, 0,};
int vis[N][N], ans, k, dis[N][N];
char mp1[N][N], mp2[N][N];

inline void bfs(int X, int Y)
{
	queue <int> qx, qy;
	qx.push(X), qy.push(Y);
	while(!qx.empty())
	{
		int x = qx.front(); qx.pop();
		int y = qy.front(); qy.pop();
		for(int i = 1; i <= 4; i ++)
		{
			int xx = x + dx[i];
			int yy = y + dy[i];
			if(vis[xx][yy] == 0)
			{
				if(mp2[xx][yy] == '1')
				{
					mp1[xx][yy] = '1';
					mp1[X][Y] = '0';
					ans += dis[x][y] + 1;
//					cout << X << "   " << Y << endl;
					return ;
				}
				vis[xx][yy] = 1;
				dis[xx][yy] = dis[x][y] + 1;
				qx.push(xx);
				qy.push(yy);
			}
		}
	}
	return ;
}

signed main()
{
	for(int i = 1; i <= 4; i ++)
		for(int j = 1; j <= 4; j ++)
			cin >> mp1[i][j];
	for(int i = 1; i <= 4; i ++)
	{
		for(int j = 1; j <= 4; j ++)
		{
			cin >> mp2[i][j];
			if(mp2[i][j] == '1') k ++;
		}
	}
	int m = k;
	while(m --)
	{
		int x = 0, y = 0;
		for(int i = 1; i <= 4; i ++)
		{
			for(int j = 1; j <= 4; j ++)
			{
				if(mp2[i][j] == '1' && mp1[i][j] == '0' && x == 0 && y == 0) x = i, y = j;
//				if(mp1[i][j] == '1' && mp2[i][j] == '1') vis[i][j] = 1;
				vis[i][j] = 0;
				dis[i][j] = 0;
			}
		}
		bfs(x, y);
	}
	cout << ans << endl;
	return 0; 
}
/*
1101
1111
1101
1011
1111
1101
0111
0111
*/

正解和第一种差不多,一开始是直接选最小的,实际上应该直接枚举两个谁和谁搭配,因为图很小,所以我们不用担心复杂度太高,我们可以把一开始就有的直接过滤掉。

#include<bits/stdc++.h>

#define int long long
#define N 10

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int k,ans = N, cnt, cnt1, a[N][N], b[N][N], vis[N * N];
struct sb{int x, y;}s1[N * N], s2[N * N];

inline int js(int x,int y,int xx,int yy) {return abs(xx - x) + abs(yy - y);}

inline void DFS(int now,int tmp)
{
	if(tmp > ans) return ;//已经劣于当前答案就直接退出
	if(now == cnt1 + 1) return ans = min(ans, tmp), void();//搜完了取最小值
	for(int i = 1;i <= cnt; i ++)//枚举每一个点
	{
		if(vis[i]) continue ;//已经有匹配的就退出
		vis[i] = 1;//否则标记
		int k = js(s2[now].x, s2[now].y, s1[i].x, s1[i].y);//计算曼哈顿距离
		DFS(now + 1, tmp + k);//搜下一个
		vis[i] = 0;//删标记
	}
}

signed main()
{
	for(int i = 1; i <= 4; i ++)
	{
		for(int j = 1; j <= 4; j ++)
		{
			char s; cin >> s; a[i][j] = s - '0';
			if(a[i][j] == 1) s1[++ cnt].x = i, s1[cnt].y = j;
		}
	}
	for(int i = 1; i <= 4; i ++)
	{
		for(int j = 1; j <= 4; j ++)
		{
			char s; cin >> s; b[i][j] = s - '0';
			if(b[i][j] == 1) s2[++ cnt1].x = i, s2[cnt1].y = j;
		}
	}
	DFS(1, 0);
	cout << ans << endl;
	return 0;
}                                                                                                                      
                                   

T2

二分 + 最小生成树。

我们发现最小联通的条件就是n-1条边可以走,那么我们直接二分加的倍数,然后sort一遍跑最小生成树,最后我们得到的就是当前的最小花费,如果要是小于x就不行,需要再加,反之则可以减少。

#include <bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, m, X, fa[N];
struct sb{int u, v, a, b, w;} e[N]; 

inline int cmp(sb a, sb b) {return a.w < b.w;}

inline int fid(int x)
{
	if(fa[x] == x) return x;
	return fa[x] = fid(fa[x]);
}

inline int check(int x)
{
	int num = 0, res = 0;
	for(int i = 1; i <= n; i ++) fa[i] = i;
	for(int i = 1; i <= m; i ++)
		e[i].w = e[i].a + e[i].b * x;
	sort(e + 1, e + m + 1, cmp);
	for(int i = 1; i <= m; i ++)
	{
		int xx = fid(e[i].u);
		int yy = fid(e[i].v);
		if(xx == yy) continue;
		res += e[i].w;
		fa[xx] = yy;
		num ++;
		if(num == n - 1) break;
	}
	if(res > X) return 1;
	else return 0;
}

signed main()
{
//	freopen("ex_trap.in", "r", stdin);
	n = read(), m = read(), X = read();
	for(int i = 1; i <= m; i ++)
		e[i] = {read(), read(), read(), read(), 0};
	int l = 0, r = X / n + 1;
	if(n > 1) r = X / (n - 1) + 1;
	while(l < r)
	{
		int mid = (l + r) >> 1;
		if(check(mid)) r = mid;
		else l = mid + 1;
//		cout << mid << endl;
	}
	cout << l << endl;
	return 0;
}

T3

建立最短路 DAG,假如存在一个点只有一个前驱,那么显然题目条件不可能满足。

假如所有点都有至少两个前驱,那么一定可以,我们可以通过构造证明:

取两条 \(1\)\(i\) 的最短路,假设两条最短路除了 \(i\) 之外最后的交点为 \(k\),令第一条路线中 \(k\) 的后继是 \(j\),假如 \(j\) 存在的另一个前驱为 \(l\),把第一条路线换成 \(1\)\(l\) 的最短路再接上第一条路线本来 \(j\)\(i\) 的路线。发现这样替换后,两个路线的最后交点会越来越提前,直到为 \(1\) 即可。

#include <bits/stdc++.h>

#define pii pair<int, int>
#define int long long
#define N 300100

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, m;
int g[N], in[N], dis[N];//g标记四不是合法
vector<pii> e[N];//存图
bitset<N> vis;//标记
priority_queue<pii, vector<pii>, greater<pii> > q;

inline void dij(int s) 
{
	for(int i = 1; i <= n; i ++) dis[i] = 1e18;//赋初值
	dis[s] = 0;
	g[s] = 1;//当前点是合法的
	for(auto it : e[s])//遍历每一个边
	{
		int v = it.first; 
		g[v] = 1;//一开始直接和1相连的边是合法的
	}
	q.push({0, s});//入列
	while (!q.empty())
	{
		int u = q.top().second;
		q.pop();
		if(vis[u]) continue ;
		if(in[u] >= 2) g[u] = 1;//如果要是当前点合法的前驱有两个就标记合法
		vis[u] = 1;
		for(auto it : e[u])//枚举边
		{
			int v = it.first;
			int w = it.second;
			if (dis[u] + w < dis[v])
			{
				dis[v] = dis[u] + w;
				in[v] = 0;
				in[v] += g[u];//累加合法的前驱
				q.push({dis[v], v});
			}
			else if(dis[u] + w == dis[v])//如果要是有相等的
				in[v] += g[u];//累加当前点的前驱
		}
	}
}

signed main()
{
	n = read(), m = read();
	for(int i = 1; i <= m; i ++)
	{
		int x = read(), y = read(), w = read();
		e[x].push_back({y, w});//存图
		e[y].push_back({x, w});
	}
	dij(1);
	for(int i = 2; i <= n; i ++)
	{
		if(in[i] >= 2) g[i] = 1;//如果大于等于2就合法
		if(!g[i]) return puts("NO"), 0;//如果有不是合法的就直接退出
	}
	puts("YES");
	return 0;
}

T4

考试的时候想到了dinic,但是没想到怎么维护最大值,最后的那个最大匹配是求出来了,但是最大值卡住了。

#include <bits/stdc++.h>

#define int long long
#define N 1001000

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

int n, m, ans, ans1, cnt, mi[N], a[N], head[N], cao[N];
struct sb{int u, v, next;} e[N];

inline void add(int u, int v)
{
	e[++ cnt].u = u;
	e[cnt].v = v;
	e[cnt].next = head[u];
	head[u] = cnt;
	return ;
}

inline int DFS(int x)
{
	for(int i = head[x]; i; i = e[i].next)
	{
		int v = e[i].v;
		if(a[v]) continue;
		a[v] = 1;
		if(! mi[v] || DFS(mi[v]))
		{
			mi[v] = x;
			return 1;
		}
	}
	return 0;
}

inline void dinic()
{
	for(int i = 1; i <= m; i ++)
	{
		memset(a, 0, sizeof a);
		if(DFS(i)) ans1 ++;
	}
	return ;
}

signed main()
{
	n = read(), m = read();
	for(int i = 1; i <= m; i ++)
	{
		int l = read(), r = read(); cao[i] = read();
		for(int j = l; j <= r; j ++)
			add(i, m + j);
	}
	dinic();
	for(int i = m + 1; i <= n + m; i ++)
	{
		if(mi[i] != 0)
			ans += cao[mi[i] - n];
	}
	cout << ans1 << " " << ans << endl;
	return 0;
}

STD:

#include <bits/stdc++.h>
using namespace std;
int read(){
	int t=0;
	static char ch;
	while(ch=getchar(),ch<'0'||ch>'9');
	t=ch^48;
	while(ch=getchar(),ch>='0'&&ch<='9') t=t*10+(ch^48);
	return t; 
}
int n,m,ans,k,pv[510],qv[510],pre[510];
int vis[510];
struct nd{
	int l,r,w;
}c[1000010];
void build(){
	queue<int>q;
	for(int i=0;i<m;i++) vis[i]=0,pre[i]=-1;
	for(int i=0;i<m;i++) if(!~pv[i]) q.push(i),vis[i]=1;
	while(q.size()){
		int u=q.front();q.pop();
		for(int j=0;j<k;j++) if(c[j].l<=u&&c[j].r>=u&&!vis[qv[j]]){
			q.push(qv[j]),vis[qv[j]]=1,pre[qv[j]]=u;
		}
	}
	for(int i=1;i<m;i++) vis[i]+=vis[i-1];
}
void work(int l,int r,int w){
	int tp=vis[r]-(l?vis[l-1]:0);
	if(!tp) return;
	c[k++]={l,r,w},ans+=w;
	int u=0,v=k-1;
	for(int i=l;i<=r;i++) if(vis[i]-(i?vis[i-1]:0)){
		u=i;
		break;
	}
	do{
		int tp=pv[u];
		pv[u]=v,qv[v]=u,u=pre[u],v=tp;
	}while(~v);
	build();
}
int main(){
	m=read(),n=read();
	for(int i=0;i<n;i++) c[i].l=read(),c[i].r=read(),c[i].w=read(),c[i].l--,c[i].r--;
	sort(c,c+n,[](nd a,nd b){return a.w>b.w;});
	for(int i=0;i<m;i++) pv[i]=qv[i]=-1;
	ans=k=0;
	build();
	for(int i=0;i<n;i++) work(c[i].l,c[i].r,c[i].w);
	printf("%d %d\n",k,ans);
}

.

posted @ 2023-07-26 22:17  北烛青澜  阅读(5)  评论(0)    收藏  举报