CCF---201809-4---再賣菜---C++---差分約束 or 記憶化深搜

試題編號: 201809-4
試題名稱: 再賣菜
時間限制: 1.0s
內存限制: 256.0MB

問題描述

  在一條街上有n個賣菜的商店,按1至n的順序排成一排,這些商店都賣一種蔬菜。
  第一天,每個商店都自己定了一個正整數的價格。店主們希望自己的菜價和其他商店的一致,第二天,每一家商店都會根據他自己和相鄰商店的價格調整自己的價格。具體的,每家商店都會將第二天的菜價設置爲自己和相鄰商店第一天菜價的平均值(用去尾法取整)。
  注意,編號爲1的商店只有一個相鄰的商店2,編號爲n的商店只有一個相鄰的商店n-1,其他編號爲i的商店有兩個相鄰的商店i-1和i+1。
  給定第二天各個商店的菜價,可能存在不同的符合要求的第一天的菜價,請找到符合要求的第一天菜價中字典序最小的一種。
  字典序大小的定義:對於兩個不同的價格序列(a1, a2, ..., an)(b1, b2, b3, ..., bn),若存在i (i>=1), 使得ai<bi,且對於所有j<i,aj=bj,則認爲第一個序列的字典序小於第二個序列。
輸入格式
  輸入的第一行包含一個整數n,表示商店的數量。
  第二行包含n個正整數,依次表示每個商店第二天的菜價。
輸出格式
  輸出一行,包含n個正整數,依次表示每個商店第一天的菜價。
樣例輸入
8
2 2 1 3 4 9 10 13
樣例輸出
2 2 2 1 6 5 16 10
數據規模和約定
  對於30%的評測用例,2<=n<=5,第二天每個商店的菜價爲不超過10的正整數;
  對於60%的評測用例,2<=n<=20,第二天每個商店的菜價爲不超過100的正整數;
  對於所有評測用例,2<=n<=300,第二天每個商店的菜價爲不超過100的正整數。
  請注意,以上都是給的第二天菜價的範圍,第一天菜價可能會超過此範圍。

思路

這裏的關鍵是差分約束系統同時存在上下界的處理
如: a <= x1 - x2 <= b
轉化爲 x1 - x2 >= a 以及 x2 - x1 >= -b (這裏變號成爲下界)
差分約束入門:點擊這裏

實現代碼(差分約束)

#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;

const int maxn = 300 + 5;

typedef pair<int, int> Edge;
vector<Edge> edge[maxn];
int n, dis[maxn], inq[maxn], a[maxn]; // dis[i]存儲的是從原點 0 到 i 之間的和

void spfa(int s) {
	memset(dis, 0, sizeof(dis));
	memset(inq, 0, sizeof(inq));
	queue<int> q;
	q.push(s); dis[s] = 0; inq[s] = 1;
	while (q.size())
	{
		int u = q.front(); q.pop(); inq[u] = 0;
		for (int i = 0; i < edge[u].size(); i++) {
			int v = edge[u][i].first, d = edge[u][i].second;
			if (dis[v] < dis[u] + d) {
				dis[v] = dis[u] + d;
				if (!inq[v]) {
					inq[v] = 1;
					q.push(v);
				}
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		cout << dis[i] - dis[i - 1] << " ";
	}
	cout << endl;
}

int main() { 
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	// 每條邊都要把 上界 轉爲負數求 下界
	// 最左和最右都要特殊處理
	edge[0].push_back(Edge(2, a[1] * 2)); // 可以理解爲 dis[2] - dis[0] >= a[1] * 2				
	edge[2].push_back(Edge(0, -(a[1] * 2 + 1)));  // 可以理解爲 dis[0] - dis[2] >= - (a[1] * 2 + 1)
	edge[n - 2].push_back(Edge(n, a[n] * 2));
	edge[n].push_back(Edge(n - 2, -(a[n] * 2 + 1)));

	for (int i = 2; i < n; i++) {
		edge[i - 2].push_back(Edge(i + 1, a[i] * 3));
		edge[i + 1].push_back(Edge(i - 2, -(a[i] * 3 + 2)));
	}

	for (int i = 1; i <= n; i++) edge[i - 1].push_back(Edge(i, 1)); // 每個店鋪都至少售出一件

	spfa(0);

	return 0;
}

實現代碼(記憶化深搜)

#include<iostream>
#include<cstring>

using namespace std;

const int maxn = 305;

int n, a[maxn], b[maxn], vis[maxn][maxn][maxn];

void dfs(int index) {
	if (vis[index][b[index - 1]][b[index - 2]]) return;
	vis[index][b[index - 1]][b[index - 2]] = 1; // 記錄是否訪問過

	int maybe = a[index - 1] * 3 - b[index - 1] - b[index - 2]; 

	if (index == n) {
		for (int i = 0; i < 3; i++) {
			if (maybe + i > 0) {
				b[n] = maybe + i;
				if ((b[n] + b[n - 1]) / 2 == a[n]) {
					for (int j = 1; j <= n; j++) cout << b[j] << " ";
					exit(0);
				}
			}
		}
		return;
	}

	for (int i = 0; i < 3; i++) {
		if (maybe + i > 0) {
			b[index] = maybe + i;
			dfs(index + 1);
		}
	}
}

int main(){
	memset(vis, 0, sizeof(vis));
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = 1; i <= 2 * a[1]; i++) { // 注意這裏要有 = , 因爲b[2] = 1 時仍成立,只需要在最後加個判斷 
		b[1] = i, b[2] = a[1] * 2 - i;
		for (int j = 0; j < 2; j++) {
			b[2] += j;
			if ((b[1] + b[2]) / 2 == a[1]) dfs(3); // 判斷
		}
	}
	return 0;
}
發佈了117 篇原創文章 · 獲贊 19 · 訪問量 6808
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章