题意:就是让你将1-n分成两部分,要求|sum(A) - sum(B)|最小,问最小是多少。
解题思路:4个连续的数肯定可以组成sum(A) == sum(B),1、2、3可以组成1+2==3,这样直接将n%4如果等于0或者等于3都可以组成sum(A) == sum(B),否则很容易想到差为1。
#include
using namespace std;
typedef unsigned long long ll;
typedef pair<ll,ll> P;
const int maxn = 1e5+100;
int main() {
//freopen("1.in", "r", stdin);
ll n;
scanf("%lld", &n);
if(n % 4 == 0 || n %4 == 3) {
puts("0");
} else {
puts("1");
}
return 0;
}
题意:n个数k种颜色,三个要求
解题思路:先判断是否可以保证每个颜色都用到,是否相同的数字可以都涂上不同的颜色。然后把每一种颜色独立出来,然后分类将颜色从1到k开始循环使用,输出就行了。
#include
using namespace std;
typedef unsigned long long ll;
typedef pair<ll,ll> P;
const int maxn = 10000;
int n, t, num[maxn], num1[maxn];
map <int, int> maps;
vector <int> ve[maxn];
int main() {
//freopen("1.in", "r", stdin);
scanf("%d%d",&n,&t);
int Max = 0;
for(int i=1;i<=n;i++) {
scanf("%d",&num[i]);
num1[i] = num[i];
maps[num[i]]++;
Max = max(Max, maps[num[i]]);
}
if(Max > t || n < t) {
puts("NO");
return 0;
}
sort(num1+1, num1+n+1);
int tot = 1;
for(int i=1;i<=n;i++) {
ve[num1[i]].push_back(tot++);
if(tot > t)
tot -= t;
}
puts("YES");
for(int i=1;i<=n;i++) {
printf("%d ", ve[num[i]][ve[num[i]].size()-1]);
ve[num[i]].pop_back();
}
return 0;
}
题意:n扇门,每个门有个生命值Ai,小明要破坏门,有个伤害值x,小美要保护门,有个回复值y,两人轮流行动,当一扇门被完全破坏之后小美无法再回复,回合数无限,现在要让小明破坏最少的门,问最少小明可以破坏多少个门。
解题思路:如果x>y那么n扇门全被破坏,否则找出生命值小于x的门,假设有c扇,可以最少被破坏的就是(c+1)/2。其实就是可以想象在c扇门中小明从前开始破坏,小美从后开始回复。
#include
using namespace std;
typedef unsigned long long ll;
typedef pair<ll,ll> P;
const int maxn = 110;
int num[maxn], cnt, n, x, y;
vector <int> ve;
int main() {
//freopen("1.in", "r", stdin);
scanf("%d%d%d",&n, &x, &y);
for(int i=1;i<=n;i++) {
scanf("%d", &num[i]);
if(num[i] <= x) {
ve.push_back(num[i]);
cnt++;
}
}
if(x > y) {
printf("%d", n);
return 0;
}
printf("%d", (cnt+1)/2);
return 0;
}
题意:现在有一个字符串s1,里面只包含字符0、1、2,现在你需要用最少的替换次数将s1变成s2,要求s2中0、1、2字符数目相等并且字典序最小。
解题思路:先算出0、1、2分别个数为cnt,
#include
using namespace std;
typedef unsigned long long ll;
typedef pair<ll, ll> P;
const int maxn = 3e5 + 100;
int n, each, cnt[300];
char s[maxn];
void change(char a, char b) {
cnt[a]++;
cnt[b]--;
}
int main() {
//freopen("1.in", "r", stdin);
scanf("%d%s", &n, s);
int len = strlen(s);
for (int i = 0; i < len; i++)
cnt[s[i]]++;
each = n / 3;
//2 to 0 1
for (int i = 0; i < len; i++) {
if (cnt['2'] <= each) break;
if (s[i] == '2') {
if (cnt['0'] < each) {
s[i] = '0';
change('0', '2');
} else if (cnt['1'] < each) {
s[i] = '1';
change('1', '2');
}
}
}
//1 to 0
for (int i = 0; i < len; i++) {
if (cnt['1'] <= each) break;
if (s[i] == '1') {
if (cnt['0'] < each) {
s[i] = '0';
change('0', '1');
}
}
}
//0 1 to 2
for (int i = len - 1; i >= 0; i--) {
if (s[i] != '2' && cnt['2'] < each) {
if (cnt[s[i]] > each) {
change('2', s[i]);
s[i] = '2';
}
}
}
//0 to 1
for (int i = len - 1; i >= 0; i--) {
if (cnt['0'] <= each) break;
if (s[i] == '0') {
if (cnt['1'] < each) {
change('1', '0');
s[i] = '1';
}
}
}
printf("%s", s);
return 0;
}
题意:现在有一个数列a,经过符合三种条件的映射可以得到数列b
解题思路:假如有一个数列a为1,2,3,4,5,6,7,8,1,那么形成的数列b只有1种(全0),也就是说两个相同的数字中间无论包含多少数字都只有一种,这就很像odt中的区间合并了,然后模仿了一下odt的区间合并就出来了。当然数字太大需要离散化一下。
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
const int mod = 998244353;
int n, a[maxn], last[maxn], pre[maxn];
struct P {
int l, r;
P(int L, int R = 0):
l(L), r(R){};
bool operator <(const P& x) const {
return l < x.l;
}
};
vector <int> ve;
set <P> tot;
void init() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
ve.push_back(a[i]);
}
//离散化
sort(ve.begin(), ve.end());
ve.erase(unique(ve.begin(), ve.end()), ve.end());
for(int i=1;i<=n;i++) {
a[i] = int(lower_bound(ve.begin(), ve.end(), a[i]) - ve.begin()) + 1;
tot.insert(P(i, i));
}
}
void deal() {
for(int i=1;i<=n;i++) {
if(!pre[a[i]]) pre[a[i]] = i;
last[a[i]] = i;
}
for(int i=0;i<ve.size();i++) {
int pos = i+1;
int l = pre[pos];
int r = last[pos];
//区间合并
set<P> :: iterator iter1, iter2, iter3;
iter1 = tot.upper_bound(P(l));
iter1--;
iter2 = tot.upper_bound(P(r, n+1));
r = (--iter2)->r;
iter2++;
l = iter1->l;
tot.erase(iter1, iter2);
tot.insert(P(l, r));
}
}
ll quick_pow(ll x, ll cnt) {
x %= mod;
ll ans = 1;
while(cnt) {
if(cnt&1) ans *= x;
x *= x;
x %= mod;
ans %= mod;
cnt >>= 1;
}
return ans;
}
int main() {
//freopen("1.in", "r", stdin);
init();
deal();
int cnt = tot.size();
ll ans = quick_pow(2, cnt-1);
printf("%lld", ans);
return 0;
}
解题思路:队友告诉我这是一个旅行商问题,但是我并不会,所以看了看了大佬怎么写的,然后自己写了一遍。首先看n和m的范围,n是16这就很明显的一个状压的标志了。整体思路就是状压+记忆化,因为复杂度的原因需要预处理每两行之间对应差的最小值。然后枚举第i行后面需要移动的所有状态。用dp[i][j]表示第i行j状态(如果j二进制状态位置为k的地方是0代表第k行需要移动到i行后面),然后递归到需要移动的第k行。
#include
using namespace std;
const int maxn = 20;
const int maxm = 1e4+100;
int num[maxn][maxm], va[maxn][maxm], va2[maxn][maxm], dp[maxn][(1<<17)];
int n, m;
void init() {
scanf("%d%d",&n, &m);
for(int i=0;i<n;i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &num[i][j]);
}
}
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
va[i][j] = va2[i][j] = INT_MAX;
for(int k=0;k<m;k++){
va[i][j] = min(va[i][j], abs(num[i][k] - num[j][k]));
if(k != 0)
va2[i][j] = min(va2[i][j], abs(num[i][k] - num[j][k-1]));//拼接成一个数组之后,拼接中间产生的差值
}
}
}
}
int row;
int dfs(int pre, int state) {
if(state == ((1<<n)-1)) return va2[row][pre];//无法再交换行的未知
if(dp[pre][state] != -1) return dp[pre][state];//这个状态曾经被查找过
dp[pre][state] = 0;
for(int i=0;i<n;i++) {
if(state & (1<<i))continue;
dp[pre][state] = max(dp[pre][state], min(dfs(i, (state|(1<<i))), va[pre][i]));//递归
}
return dp[pre][state];
}
int main() {
//freopen("1.in", "r", stdin);
init();
int ans = 0;
for(row=0; row<n; row++) {
memset(dp, -1, sizeof(dp));
ans = max(ans, dfs(row, (1<<row)));//后面可以交换的所有状态
}
printf("%d", ans);
return 0;
}