C. Column Swapping

这篇博客讨论了C. Column Swapping问题,即如何通过交换一次列使得n×m网格中每行单调递增。文章首先介绍了错误的思路,然后提出了一个AC(Accepted)解决方案:先对每行单元格按值和列位置排序,找出需要交换的列并尝试交换,最后验证是否满足条件。若无法通过一次交换实现所有行的单调递增,则输出-1。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:C. Column Swapping

考点:思维

题意:给定一个 n × m n\times m n×m 的网格,每个单元格里都有一个正整数,问如果只交换一次列,能否让每一行都是单调递增的,及 a i , j ≥ a i , j − 1 a_{i,j} \geq a_{i,j-1} ai,jai,j1 。交换的列不一定是不同的列,交换时是整体交换,并不是交换某一行的列。

w a   13 \color{red}{wa\ 13} wa 13 的思路:先遍历每一行,看是否存在需要交换的列,例如: a i , j < a i , j − 1 a_{i,j} < a_{i,j-1} ai,j<ai,j1,记录下需要交换的列的数目 x x x,以及需要交换的列的位置。
x = { 0 不存在交换的列,直接输出(1,1) 1 只有一个位置需要交换,因为其它的列都是有序的,所以只能交换(j-1,j),交换完再去验证是否单调,交换其它的列必定不会有序  2 有两个位置需要交换,第一个位置的前一个列与第二个位置的后一个列经行进行交换,交换完再去验证是否单调 > 2 怎么交换都不可能让每行单调递增,直接输出-1 x = \begin{cases} 0& \text{不存在交换的列,直接输出(1,1)} \\1 &\text{只有一个位置需要交换,因为其它的列都是有序的,所以只能交换(j-1,j),交换完再去验证是否单调,交换其它的列必定不会有序 } \\2 &\text{有两个位置需要交换,第一个位置的前一个列与第二个位置的后一个列经行进行交换,交换完再去验证是否单调} \\>2 &\text{怎么交换都不可能让每行单调递增,直接输出-1} \end{cases} x=012>2不存在交换的列,直接输出(1,1)只有一个位置需要交换,因为其它的列都是有序的,所以只能交换(j-1,j),交换完再去验证是否单调,交换其它的列必定不会有序 有两个位置需要交换,第一个位置的前一个列与第二个位置的后一个列经行进行交换,交换完再去验证是否单调怎么交换都不可能让每行单调递增,直接输出-1
此思路存在许多的问题,当序列为 1   2   4   3   3   5 1\ 2\ 4\ 3\ 3\ 5 1 2 4 3 3 5 或者 1   3   3   2   4   4 1\ 3\ 3\ 2\ 4\ 4 1 3 3 2 4 4 时,只有一个交换点,但是,交换的列并不是我们想要的。

A C \color{green}{AC} AC 的思路:先记录每个行单元格的值与列位置,每行分别递增排序 (第一排序为单元格中的值升序排,第二排序为单元格所在的列升序排) ,遍历每行的元素,找出当前位置与之前记录位置不同的列,然后交换最前和最后的两个列,验证每行是否都单调递增,如果是,输出两列的值,如果不是,输出 − 1 -1 1

为什么这样是对的?因为排序后存在位置的变化,也就是说要单调递增必须交换某些列,所以找到最前和最后需要交换的位置,由于只能交换一次,如果交换后还存在不递增的情况,则表明还需要交换其它的列,但是仅有的一次机会我们已经使用完了,所以直接输出 − 1 -1 1

#include<stdio.h>
#include<vector>
#include<map>
#include<algorithm>

using namespace std;
const int N = 2e5 + 10;
vector<pair<int,int> >a[N],b[N];
int n,m;

int check(int x,int y) {

	for(int i=0;i<n;i++) swap(b[i][x],b[i][y]);
	
	int flag = 0;
	for(int i=0;i<n;i++) {
		for(int j=1;j<m;j++) {
			if(b[i][j].first < b[i][j-1].first) {
				flag = 1;
				break;
			}
		}
		if(flag) break;
		
	}
	for(int i=0;i<n;i++) swap(b[i][x],b[i][y]);
	
	return flag;
}

int main(){
	int t;
	scanf("%d",&t);
	
	while(t --) {
		scanf("%d %d",&n,&m);
		
		for(int i=0;i<=n;i++) {
			a[i].clear();
			b[i].clear();
		}
		
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				int c;
				scanf("%d",&c);
				a[i].push_back({c,j});
				b[i].push_back({c,j});
			}
		}
		
		for(int i=0;i<n;i++) sort(a[i].begin(),a[i].end());
		
		int x = -1,y = -1;
		
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				if(a[i][j].second != j) {
					if(x == -1) x = j;
					y = j;
				}
			}
		} 
		
		if(x == -1) printf("1 1\n");
		else if(check(x,y) == 0) printf("%d %d\n",x+1,y+1);
		else printf("-1\n");
	}
	
	return 0;
} 
#include <graphics.h> #include <conio.h> #include <vector> #include <ctime> #include <string> #include <sstream> #include <windows.h> #include <chrono> // 修复:正确的头文件名 #include <algorithm> #include <atomic> using namespace std; using namespace std::chrono; const int ROW = 8; const int COL = 10; const int BLOCK_SIZE = 80; const int WINDOW_WIDTH = COL * BLOCK_SIZE; const int WINDOW_HEIGHT = ROW * BLOCK_SIZE + 120; const int COLORS[] = {RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, BROWN, LIGHTBLUE}; const int NUM_COLORS = sizeof(COLORS) / sizeof(COLORS[0]); // Exit button position and state const int EXIT_BTN_WIDTH = 120; const int EXIT_BTN_HEIGHT = 40; const int EXIT_BTN_X = WINDOW_WIDTH - EXIT_BTN_WIDTH - 20; const int EXIT_BTN_Y = WINDOW_HEIGHT - EXIT_BTN_HEIGHT - 15; bool exitBtnHovered = false; bool exitBtnPressed = false; vector<vector<int>> grid(ROW, vector<int>(COL)); atomic<int> score(0); bool gameOver = false; bool quitGame = false; int selectedX = -1, selectedY = -1; steady_clock::time_point lastClickTime = steady_clock::now(); const int MIN_CLICK_INTERVAL = 50; bool isAnimating = false; // High-resolution timer class HighResTimer { public: void start() { startTime = steady_clock::now(); } double elapsed() const { return duration_cast<duration<double>>(steady_clock::now() - start极Time).count(); } private: steady_clock::time_point startTime; }; // Integer to string conversion string intToString(int value) { stringstream ss; ss << value; return ss.str(); } // Draw exit button void drawExitButton() { if (exitBtnHovered) { setfillcolor(LIGHTGRAY); } else { setfillcolor(DARKGRAY); } // Draw button background solidrectangle(EXIT_BTN_X, EXIT_BTN_Y, EXIT_BTN_X + EXIT_BTN_WIDTH, EXIT_BTN_Y + EXIT_BTN_HEIGHT); // Draw button border setlinecolor(WHITE); rectangle(EXIT_BTN_X, EXIT_BTN_Y, EXIT_BTN_X + EXIT_BTN_WIDTH, EXIT_BTN_Y + EXIT_BTN_HEIGHT); // Draw button text settextcolor(exitBtnPressed ? RED : WHITE); settextstyle(24, 0, _T("Arial")); outtextxy(EXIT_BTN_X + 25, EXIT_BTN_Y + 8, "Exit Game"); } // Initialize game grid void initGrid() { srand(static_cast<unsigned>(time(0))); // Generate initial grid without matches bool hasMatches = true; while (hasMatches) { hasMatches = false; for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL; ++j) { grid[i][j] = rand() % NUM_COLORS; } } // Check horizontal matches for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL - 2; ++j) { if (grid[i][j] == grid[i][j+1] && grid[i][j] == grid[i][j+2]) { hasMatches = true; break; } } if (hasMatches) break; } // Check vertical matches if (!hasMatches) { for (int j = 0; j < COL; ++j) { for (int i = 0; i < ROW - 2; ++i) { if (grid[i][j] == grid[i+1][j] && grid[i][j] == grid[i+2][j]) { hasMatches = true; break; } } if (hasMatches) break; } } } } // Optimized grid drawing with exit button void drawGridPartial(int highlightX = -1, int highlightY = -1) { // Draw only highlighted block if specified if (highlightX >= 0 && highlightY >= 0) { int x = highlightX, y = highlightY; // Clear original position setfillcolor(BLACK); solidrectangle(x * BLOCK_SIZE, y * BLOCK_SIZE, (x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE); // Draw new block if (grid[y][x] != -1) { setfillcolor(COLORS[grid[y][x]]); solidrectangle(x * BLOCK_SIZE, y * BLOCK_SIZE, (x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE); setlinecolor(BLACK); rectangle(x * BLOCK_SIZE, y * BLOCK_SIZE, (x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE); } // Highlight selected block if (x == selectedX && y == selectedY) { setlinecolor(WHITE); setlinestyle(PS_SOLID, 3); rectangle(x * BLOCK_SIZE + 2, y * BLOCK_SIZE + 2, (x + 1) * BLOCK_SIZE - 2, (y + 1) * BLOCK_SIZE - 2); setlinestyle(PS_SOLID, 1); } return; } // Full screen redraw cleardevice(); settextstyle(24, 0, _T("Arial")); // Draw blocks for (极int i = 0; i < ROW; ++i) { for (int j = 0; j < COL; ++j) { if (grid[i][j] != -1) { setfillcolor(COLORS[grid[i][j]]); solidrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE); setlinecolor(BLACK); rectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE); // Highlight selected block if (i == selectedY && j == selectedX) { setlinecolor(WHITE); setlinestyle(PS_SOLID, 3); rectangle(j * BLOCK_SIZE + 2, i * BLOCK_SIZE + 2, (j + 1) * BLOCK_SIZE - 2, (i + 1) * BLOCK_SIZE - 2); setlinestyle(PS_SOLID, 1); } } else { // Draw empty block (transparent) setfillcolor(BLACK); setlinecolor(DARKGRAY); rectangle(j * BLOCK_SIZE, i * BLOCK_SIZE, (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE); } } } // Draw score panel setfillcolor(DARKGRAY); solidrectangle(0, ROW * BLOCK_SIZE, WINDOW_WIDTH, WINDOW_HEIGHT); settextcolor(WHITE); string scoreStr = "Score: " + intToString(score); outtextxy(20, ROW * BLOCK_SIZE + 20, scoreStr.c_str()); // Draw exit button drawExitButton(); // Game over message if (gameOver) { settextcolor(RED); settextstyle(36, 0, _T("Arial")); outtextxy(WINDOW_WIDTH/2 - 120, WINDOW_HEIGHT/2 - 30, "Game Over!"); settextstyle(24, 0, _T("Arial")); string finalScore = "Final Score: " + intToString(score); outtextxy(WINDOW_WIDTH/2 - 100, WINDOW_HEIGHT/2 + 20, finalScore.c_str()); } } // Check for possible moves bool hasPossibleMoves() { // Horizontal swap check for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL - 1; ++j) { // Try swapping swap(grid[i][j], grid[i][j+1]); // Check if swap creates a match bool matchFound = false; // Check row matches (optimized area) for (int r = max(0, i-1); r <= min(ROW-1, i+1); ++r) { for (int c = max(0, j-2); c <= min(COL-3, j+2); ++c) { if (c < 0 || c+2 >= COL) continue; if (grid[r][c] != -1 && grid[r][c] == grid[r][c+1] && grid[r][c] == grid[r][c+2]) { matchFound = true; break; } } if (matchFound) break; } // Check column matches if (!matchFound) { for (int c = max(0, j-1); c <= min(COL-1, j+1); ++c) { for (int r = max(0, i-2); r <= min(ROW-3, i+2); ++r) { if (r < 0 || r+2 >= ROW) continue; if (grid[r][c] != -1 && grid[r][c] == grid[r+1][c] && grid[r][c] == grid[r+2][c]) { matchFound = true; break; } } if (matchFound) break; } } // Restore swap swap(grid[i][j], grid[i][j+1]); if (matchFound) return true; } } // Vertical swap check for (int j = 0; j < COL; ++j) { for (int i = 0; i < ROW - 1; ++i) { // Try swapping swap(grid[i][j], grid[i+1][j]); // Check if swap creates a match bool matchFound = false; // Check row matches for (int r = max(0, i-1); r <= min(ROW-1, i+1); ++r) { for (int c = max(0, j-2); c <= min(COL-3, j+2); ++c) { if (c < 0 || c+2 >= COL) continue; if (grid[r][c] != -1 && grid[r][c] == grid[r][c+1] && grid[r][c] == grid[r][c+2]) { matchFound = true; break; } } if (matchFound) break; } // Check column matches if (!matchFound) { for (int c = max(0, j-1); c <= min(COL-1, j+1); ++c) { for (int r = max(0, i-2); r <= min(ROW-3, i+2); ++r) { if (r < 0 || r+2 >= ROW) continue; if (grid[r][c] != -1 && grid[r][c] == grid[r+1][c] && grid[r][c] == grid[r+2][c]) { matchFound = true; break; } } if (matchFound) break; } } // Restore swap swap(grid[i][j], grid[i+1][j]); if (matchFound) return true; } } return false; } // Process matches and update score int processMatches() { vector<vector<bool>> toRemove(ROW, vector<bool>(COL, false)); int matchCount = 0; // Horizontal match check for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL - 2; ) { if (grid[i][j] != -1 && grid[i][j] == grid[i][j + 1] && grid[i][j] == grid[i][j + 2]) { int k = j + 2; while (k < COL && grid[i][j] == grid[i][k]) { k++; } for (int pos = j; pos < k; ++pos) { toRemove[i][pos] = true; matchCount++; } j = k; // Skip checked positions } else { j++; } } } // Vertical match check for (int j = 0; j < COL; ++j) { for (int i = 0; i < ROW - 2; ) { if (grid[i][j] != -1 && grid[i][j] == grid[i + 1][j] && grid[i][j] == grid[i + 2][j]) { int k = i + 2; while (k < ROW && grid[i][j] == grid[k][j]) { k++; } for (int pos = i; pos < k; ++pos) { toRemove[pos][j] = true; matchCount++; } i = k; // Skip checked positions } else { i++; } } } // Update score if (matchCount > 0) { static int comboCount = 0; comboCount++; int comboBonus = max(0, comboCount - 1) * 5; score += matchCount * 10 + comboBonus; } else { // Reset combo count static int comboCount = 0; comboCount = 0; } // Remove matched blocks for (int i = 0; i < ROW; ++i) { for (int j = 0; j < COL; ++j) { if (toRemove[i][j]) { grid[i][j] = -1; } } } return matchCount; } // Block falling animation void dropBlocksAnimated() { HighResTimer timer; timer.start(); // Track new positions for each column vector<vector<int>> newPositions(COL); vector<vector<int>> temp(COL); // Prepare falling data for (int j = 0; j < COL; ++j) { // Collect non-empty blocks (bottom to top) for (int i = ROW - 1; i >= 0; --i) { if (grid[i][j] != -1) { temp[j].push_back(grid[i][j]); } } // Generate new blocks int newBlocks = ROW - temp[j].size(); for (int i = 0; i < newBlocks; ++i) { temp[j].push_back(rand() % NUM_COLORS); } // Calculate new positions for (int i = 0; i < ROW; ++i) { newPositions[j].push_back(temp[j][ROW - 1 - i]); } } // Animation duration (seconds) const double animationDuration = 0.3; vector<vector<int>> originalGrid = grid; // Save original state while (true) { double elapsed = timer.elapsed(); if (elapsed >= animationDuration) break; double progress = min(1.0, elapsed / animationDuration); // Update animation frame for (int j = 0; j < COL; ++j) { for (int i = 0; i < ROW; ++i) { // If block needs to fall if (originalGrid[i][j] != -1 && newPositions[j][i] != originalGrid[i][j]) { // Calculate falling distance int targetY = i; while (targetY < ROW && grid[targetY][j] != newPositions[j][i]) { targetY++; } if (targetY < ROW) { // Calculate current position double currentY = i + (targetY - i) * progress; // Draw falling block setfillcolor(COLORS[originalGrid[i][j]]); solidrectangle(j * BLOCK_SIZE, static_cast<int>(currentY * BLOCK_SIZE), (j + 1) * BLOCK_SIZE, static_cast<int>((currentY + 1) * BLOCK_SIZE)); setlinecolor(BLACK); rectangle(j * BLOCK_SIZE, static_cast<int>(currentY * BLOCK_SIZE), (j + 1) * BLOCK_SIZE, static_cast<int>((currentY + 1) * BLOCK_SIZE)); } } } } FlushBatchDraw(); Sleep(10); } // Apply final state for (int j = 0; j < COL; ++j) { for (int i = 0; i < ROW; ++i) { grid[i][j] = newPositions[j][i]; } } } // Handle mouse clicks (including exit button) void handleMouseClick() { // Ignore input during animation if (isAnimating) return; if (MouseHit()) { MOUSEMSG msg = GetMouseMsg(); // Check click interval auto now = steady_clock::now(); auto elapsed = duration_cast<milliseconds>(now - lastClickTime).count(); if (elapsed < MIN_CLICK_INTERVAL) { return; } lastClickTime = now; // Check if mouse is over exit button bool prevHovered = exitBtnHovered; exitBtnHovered = (msg.x >= EXIT_BTN_X && msg.x <= EXIT_BTN_X + EXIT_BTN_WIDTH && msg.y >= EXIT_BTN_Y && msg.y <= EXIT_BTN_Y + EXIT_BTN_HEIGHT); // Redraw button if hover state changed if (exitBtnHovered != prevHovered) { drawExitButton(); FlushBatchDraw(); } // Handle exit button click if (msg.uMsg == WM_LBUTTONDOWN && exitBtnHovered) { exitBtnPressed = true; drawExitButton(); FlushBatchDraw(); } else if (msg.uMsg == WM_LBUTTONUP && exitBtnPressed) { exitBtnPressed = false; if (exitBtnHovered) { // Quit game quitGame = true; return; } drawExitButton(); FlushBatchDraw(); } // Handle grid clicks if (msg.uMsg == WM_LBUTTONDOWN) { int x = msg.x / BLOCK_SIZE; int y = msg.y / BLOCK_SIZE; // Ensure click is within grid if (x >= 0 && x < COL && y >= 0 && y < ROW && grid[y][x] != -1) { // First click or change selection if (selectedX == -1) { selectedX = x; selectedY = y; drawGridPartial(x, y); // Partial redraw FlushBatchDraw(); } // Click same block - deselect else if (selectedX == x && selectedY == y) { selectedX = -1; selectedY = -1; drawGridPartial(x, y); // Partial redraw FlushBatchDraw(); } // Second click (swap) else if ((abs(selectedX - x) == 1 && selectedY == y) || (abs(selectedY - y) == 1 && selectedX == x)) { // Set animation state isAnimating = true; // Swap animation int prevX = selectedX, prevY = selectedY; // Draw swap animation HighResTimer swapTimer; swapTimer.start(); const double swapDuration = 0.2; while (true) { double elapsed = swapTimer.elapsed(); if (elapsed >= swapDuration) break; double progress = min(1.0, elapsed / swapDuration); // Calculate intermediate position int offsetX = static_cast<int>((x - prevX) * progress * BLOCK_SIZE); int offsetY = static_cast<int>((y - prevY) * progress * BLOCK_SIZE); // Clear original position setfillcolor(BLACK); solidrectangle(prevX * BLOCK_SIZE, prevY * BLOCK_SIZE, (prevX + 1) * BLOCK_SIZE, (prevY + 1) * BLOCK_SIZE); solidrectangle(x * BLOCK_SIZE, y * BLOCK_SIZE, (极x + 1) * BLOCK_SIZE, (y + 1) * BLOCK_SIZE); // Draw moving blocks setfillcolor(COLORS[grid[prevY][prevX]]); solidrectangle(prevX * BLOCK_SIZE + offsetX, prevY * BLOCK_SIZE + offsetY, (prevX + 1) * BLOCK_SIZE + offsetX, (prevY + 1) * BLOCK_SIZE + offsetY); setfillcolor(COLORS[grid[y][x]]); solidrectangle(x * BLOCK_SIZE - offsetX, y * BLOCK_SIZE - offsetY, (x + 1) * BLOCK_SIZE - offsetX, (y + 1) * BLOCK_SIZE - offsetY); FlushBatchDraw(); Sleep(10); } // Actually swap blocks swap(grid[prevY][prevX], grid[y][x]); // Check if match is formed bool matched = false; if (processMatches() > 0) { matched = true; } if (!matched) { // Revert swap if no match swap(grid[prevY][prevX], grid[y][x]); drawGridPartial(prevX, prevY); drawGridPartial(x, y); FlushBatchDraw(); } else { // Process chain reactions while (true) { // Drop blocks with animation dropBlocksAnimated(); // Check for new matches if (processMatches() == 0) break; } // Check if game is over gameOver = !hasPossibleMoves(); } // Reset selection selectedX = selectedY = -1; // End animation isAnimating = false; } // Click different position, change selection else { // Clear old selection int oldX = selectedX, oldY = selectedY; selectedX = x; selectedY = y; // Partial redraw if (oldX >= 0 && oldY >= 0) { drawGridPartial(oldX, oldY); } drawGridPartial(x, y); FlushBatchDraw(); } } // Click outside grid, reset selection else if (selectedX >= 0 || selectedY >= 0) { int oldX = selectedX, oldY = selectedY; selectedX = selectedY = -1; // Partial redraw if (oldX >= 0 && oldY >= 0) { drawGridPartial(oldX, oldY); } FlushBatchDraw(); } } } } int main() { initgraph(WINDOW_WIDTH, WINDOW_HEIGHT); initGrid(); BeginBatchDraw(); // High-precision timer HighResTimer frameTimer; const double targetFPS = 120.0; const double frameTime = 1.0 / targetFPS; while (!gameOver && !quitGame) { frameTimer.start(); if (!isAnimating) { drawGridPartial(); // Partial redraw mode } handleMouseClick(); FlushBatchDraw(); // Precise frame rate control double elapsed = frameTimer.elapsed(); if (elapsed < frameTime) { int sleepTime = static_cast<int>((frameTime - elapsed) * 1000); if (sleepTime > 0) { Sleep(sleepTime); } } } // Game over state if (!quitGame) { drawGridPartial(); FlushBatchDraw(); // Wait for key press to exit while (!_kbhit() && !quitGame) { Sleep(100); } } EndBatchDraw(); closegraph(); return 0; }
最新发布
06-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值