ACM日常训练日记——7.23

  • Atcoder训练
    1. Flipping Signs
      思维
      通过打表观察发现,当负数为偶数时可以全部转化为正,不为偶数时,会留下一个负数,我们取绝对值最小的即可。
#include <bits/stdc++.h>

using namespace std;

using  ll=long long;
ll cd[1000010];
int main(){
	ll n;
	ll sum=0;
	ll mi=2e18;
	cin>>n;
	vector<ll>v(n+1);
	ll fu=0;
	for(int i=1;i<=n;i++){
		cin>>v[i];
		if(v[i]<0)fu++;
		sum+=abs(v[i]);
		mi=min(mi,abs(v[i]));
	}
	if(fu%2==0){
		cout<<sum;
	}else
		cout<<sum-2*mi;
}
  1. Rectangle Cutting
    思维
    通过该点怎么划分的面积小的最大,只要不是矩形最中间的点,都只有一种可能,其他情况都能分一半。
#include <bits/stdc++.h>
using namespace std;

using ll =long long;
ll prefix[1000010];
ll v[1000010];
set<ll>st;
int main(){
	double a,b,c,d;
	cin>>a>>b>>c>>d;
	if(c*2!=a||d*2!=b){
	printf("%.6lf 0",a*b/2);
	}else
	printf("%.6lf 1",a*b/2);
	
}
  1. Maze Master
    一个简单的BFS,但是我卡了半小时也没过,原因在于我每次BFS都是拿一个点去搜索,这可能会导致无法找到最远的两个点之间的最大距离。为了找到迷宫中任意两点之间的最大距离,需要对每一个可通行的点都进行BFS,并记录下所有点之间的距离,然后从中找出最大值。
    举个例子来说明这个问题:
    假设有一个迷宫如下:
5 5
.....
.###.
.....
.###.
.....

在这个迷宫中,如果从左上角的点开始BFS,你可能会找到一个相对较大的距离,但这个距离并不是整个迷宫中最大的距离。
实际上,最大的距离应该是从左上角到右下角,或者从左下角到右上角。
为了找到整个迷宫中任意两点之间的最大距离,需要对每一个可通行的点都进行BFS,并记录下所有点之间的距离,然后从中找出最大值。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

ll dx[] = {0, 0, -1, 1};
ll dy[] = {1, -1, 0, 0};
char mp[25][25];
ll vis[25][25];
ll ans = 0;
ll d[25][25];
struct Node {
    int x;
    int y;
} v[25 * 25];
int n, m, k = 1;

bool inmp(int x, int y) {
    return x > 0 && x <= n && y > 0 && y <= m;
}

void bfs(int tx, int ty) {
    memset(vis, 0, sizeof vis);
    memset(d, 0, sizeof d);
    queue<pair<ll, ll>> q;
    d[tx][ty] = 0;
    vis[tx][ty] = true;
    q.push({tx, ty});
    while (q.size()) {
        ll x = q.front().first, y = q.front().second;
        q.pop();
        for (int i = 0; i < 4; i++) {
            ll nx = x + dx[i], ny = y + dy[i];
            if (inmp(nx, ny) && mp[nx][ny] == '.' && !vis[nx][ny]) {
                d[nx][ny] = d[x][y] + 1;
                vis[nx][ny] = true;
                q.push({nx, ny});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mp[i][j];
            if (mp[i][j] == '.')
                v[k].x = i, v[k++].y = j;
        }
    }
//这里我出问题了,就直接cout<<ans,它只能找到从某个点出发的最大距离,而不能保证这个距离是整个迷宫中任意两点之间的最大距离。

    for (int i = 1; i < k; i++) {
        bfs(v[i].x, v[i].y);
        for (int j = 1; j < k; j++) {
            if (i != j) {
                ans = max(ans, d[v[j].x][v[j].y]);
            }
        }
    }
    cout << ans << endl;
    return 0;
}

  1. Dubious Document 2
    差一分,思维不对,我贪心地从后面开始找但是存在前面为优的情况,死活过不去,
    看了别人代码,找出所有可以插空的位置,然后全部尝试,比较字典序。
    贴上我自己写的代码
#include <bits/stdc++.h>

using namespace std;

int main() {
	string s1, s2;
	cin >> s1 >> s2;
	int le = s1.length();
	int le2 = s2.length();
	string best_s = "";
	
	for (int i = 0; i <= le - le2; ++i) {
		bool can_insert = true;
		string temp_s = s1;
		for (int j = 0; j < le2; ++j) {
			if (s1[i + j] != '?' && s1[i + j] != s2[j]) {
				can_insert = false;
				break;
			}
			temp_s[i + j] = s2[j];
		}
		if (can_insert) {
			for (int k = 0; k < le; ++k) {
				if (temp_s[k] == '?') {
					temp_s[k] = 'a';
				}
			}
			if (best_s == "" || temp_s < best_s) {
				best_s = temp_s;
			}
		}
	}
	
	if (best_s == "") {
		cout << "UNRESTORABLE";
	} else {
		cout << best_s;
	}
	
	return 0;
}
  • 动态规划专题
    第一类 线性的有明确的起点和终点,每一个前面的状态都是可选的,特殊不能选的设置单向的,
    1. [NOIP2002]过河卒
      入门的动态规划题,转移方程就是f[i][j] = f[i - 1][j]+f[i][j - 1],加了不可以走的点即马走的点。
      把马走过的点f[i][j]设置为0,即为不可以通过。然后两重for循环一行一行计算得出答案,这里需要注意一下越界问题就行。
#include <bits/stdc++.h>
using namespace std;

using ll = long long;

const int MAXN = 21;
ll f[MAXN][MAXN]; // 从0,0出发到i,j的路径条数
bool v[MAXN][MAXN]; // 标记马的控制点
int xx[] = {2, 1, -1, -2, -1, -2, 1, 2};
int yy[] = {1, 2, 2, 1, -2, -1, -2, -1};
int main() {
	int n, m, x, y;
	cin >> n >> m >> x >> y;
	// 初始化f数组
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			f[i][j] = 0;
		}
	}
	// 标记马的控制点
	v[x][y] = true;
	for (int i = 0; i < 8; i++) {
		int tx = x + xx[i];
		int ty = y + yy[i];
		if (tx >= 0 && tx <= n && ty >= 0 && ty <= m) {
			v[tx][ty] = true;
		}
	}
	// 动态规划计算路径条数
	f[0][0] = 1;
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			if (v[i][j]) continue; // 如果是马的控制点,跳过
			if (i > 0) f[i][j] += f[i - 1][j];
			if (j > 0) f[i][j] += f[i][j - 1];
		}
	}
	cout << f[n][m] << endl;
	return 0;
}
  1. [NOIP2008]传球游戏
    跟上面的过河卒类似,但是我们需要搞清楚i,j的顺序以及,边界和初始状态
    状态转移方程为 f[i][j]=f[i-1][j-1]+f[i-1][j+1]由于是一个圈,当j=n时,j+1=1,当j=1时,j-1=n;
#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const ll MAXN = 300;
ll f[MAXN][MAXN]; // 第i次传球,球在第j个人手里的方案数

int main() {
	int n, m;
	cin >> n >> m;
	//初始化
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			f[i][j] = 0;
		}
	}
	// 即前后人手里的方案数
	//由于是一个圈,当j==n时,j+1=1,当j==1时,j-1=n;
	//初始状态,边界 f[0][1]=1
	f[0][1] = 1;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n; j++) {
			if (j == n ) {
				f[i][j] = f[i-1][j-1]+f[i-1][1];
			}
			else if (j == 1) {
				f[i][j] =  f[i-1][n]+f[i-1][j+1];
			} 
			else if(j!=n&&j!=1){
				f[i][j] = f[i - 1][j-1]+f[i-1][j+1];
			}
		}
	}
	cout << f[m][1] << endl;
	return 0;
}
  • Codeforces
    1. Diagonals
      数学题,需要正确理解题目意思,可以多画几个图就找到规律了——中间可以放n个点,然后两边递减n-1,以此类推,最后会剩下2个位置,特判一下就行
#include <bits/stdc++.h>
using namespace std;

using ll =long long;
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	ll t;
	cin>>t;
	while(t--){
		ll a,k;
		cin>>a>>k;
		if(k==0){
			cout<<0<<'\n';
		}else{
			if(k<=a){
				cout<<1<<'\n';
			}else{
				ll cnt=a;
				ll ans=1;
				k-=cnt;
				cnt--;
				int pd=0;
				while(k>0&&cnt>0){
					k-=cnt;
					pd++;
					ans++;
					if(pd==2)cnt--,pd=0;
				}
				if(k>=2)cout<<ans+2<<'\n';
				else cout<<ans<<'\n';
			}
		}
	}
}
  1. Bouquet Easy Version
    这道题很有意思,思路算连续区间和<=m的最大值一开始我想从前往后加,分析太慢,于是乎想到了双指针,觉得可行交了一发,超时了
    后面想到避免重复计算:在当前的实现中,每次从新的起点开始时,都会重新计算从该起点到每个后续点的总花费和总花瓣数。我们可以通过记录前缀和来避免这种重复计算。在确定了起点后,可以使用二分查找来快速找到满足条件的最远点。
#include <bits/stdc++.h>
using namespace std;

using ll = long long;

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t;
	cin >> t;
	while (t--) {
		ll n, m;
		cin >> n >> m;
		vector<ll> v(n);
		for (int i = 0; i < n; i++) cin >> v[i];
		sort(v.begin(), v.end());
		
		vector<ll> prefix_sum(n + 1, 0);
		for (int i = 0; i < n; i++) {
			prefix_sum[i + 1] = prefix_sum[i] + v[i];
		}
		
		ll ans = 0;
		for (int i = 0; i < n; i++) {
			ll left = i, right = n - 1;
			while (left <= right) {
				ll mid = (left + right) / 2;
				if (v[mid] - v[i] <= 1 && prefix_sum[mid + 1] - prefix_sum[i] <= m) {
					ans = max(ans, prefix_sum[mid + 1] - prefix_sum[i]);
					left = mid + 1;
				} else {
					right = mid - 1;
				}
			}
		}
		cout << ans << '\n';
	}
}


posted @ 2024-07-24 09:56  冬天的睡袋  阅读(19)  评论(0编辑  收藏  举报