P6363 软件工程实习
思路
- 分析:这一题就是一个模拟题,按照他说的过程一步一步的来
代码
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define db double
#define INF 0x3f3f3f3f
const int mxn = 2005;
int n, k;
struct Node
{
db a, b;
int x;
int sum;
bool operator < (const Node z) const
{
if(sum == z.sum)
return x < z.x;
return sum > z.sum;
}
} st[mxn];
db mrk[mxn][mxn];
db ar[mxn], arr[mxn];
int brr[mxn];
char c[2];
int main()
{
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; i ++)
{
scanf("%lf %s", &st[i].a, c), st[i].a *= 0.6;
st[i].x = c[0] - 'A' + 1;
}
for(int i = 1; i <= k; i ++)
{
for(int j = 1; j <= k; j ++)
scanf("%lf", &mrk[i][j]), ar[j] += mrk[i][j];
}
for(int i = 1; i <= k; i ++)
{
ar[i] /= k;
}
for(int i = 1; i <= k; i ++)
{
for(int j = 1; j <= k; j ++)
{
if(mrk[i][j] >= ar[j] - 15 && mrk[i][j] <= ar[j] + 15)
arr[j] += mrk[i][j], brr[j] ++;
}
}
for(int i = 1; i <= k; i ++)
{
arr[i] = round(arr[i]/brr[i]);
}
for(int i = 1; i <= n; i ++)
{
st[i].b = arr[st[i].x] * 0.4;
st[i].sum = round(st[i].a + st[i].b);
}
sort(st + 1, st + 1 + n);
for(int i = 1; i <= n; i ++)
{
printf("%d %c\n", st[i].sum, st[i].x + 'A' - 1);
}
return 0;
}
P6364 1024 程序员节发橙子
思路
- 这一题我们如果用贪心的思路来做的话,初始的时候每个人一个橘子,先从左向右扫一遍,如果当前的这个人的分数比左边的高的话,就在左边的人的橘子数量上加1,如果过相同的话,直接 让当前这个人的橘子的数量等于左边的那个同学的数量。
- 从左向右向左在扫一遍,如果当前这个同学的分数比右边同学的分数高,并且橘子的数量却是小于等于相邻右边的那个同学的话,这个时候在让右边的同学的数量上+1,就行了
- 差分约束:这一题如果用差分约束的话,它的本质的就是解不等式,
- 首先这一题考虑,让我们求总共最小的橘子的数量,那么我们应该用 Dijkstra或Spfa 跑长短路,所以我们要把所有的不等式转化 “ >= ”的形式,然后建立边
- 那么我们可以从题意上找出隐含的不等式,如果 当前同学(v 为其橘子的数量)的分数 == 左边同学(u 为其橘子的数量)的分数的,那么说明 v == u, 那么我们建立权值为为0的双向边,如果分高于的话v - u >= 1 建立 i-1 -> i 权值为1 的边,如果分低于的话那么 u - v >= 1,建立 i -> i -1 权值为1的反向边
代码(贪心)
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
const int mxn = 1e6 + 10;
int ar[mxn];
ll br[mxn];
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &ar[i]), br[i] = 1;
for(int i = 2; i <= n; i ++)
{
if(ar[i] > ar[i - 1])
br[i] = br[i - 1] + 1;
else if(ar[i] == ar[i - 1])
br[i] = br[i - 1];
}
for(int i = n - 1; i >= 1; i --)
{
if(ar[i] > ar[i + 1] && br[i] <= br[i + 1])
br[i] = br[i + 1] + 1;
else if(ar[i] == ar[i + 1])
br[i] = max(br[i], br[i + 1]);
}
ll sum = 0;
for(int i = 1; i <= n; i ++)
sum += br[i];
printf("%lld\n", sum);
return 0;
}
代码(最长路-差分约束)
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define db double
#define INF 0x3f3f3f3f
const int mxn = 1e6 + 10;
int ar[mxn];
int n;
struct Edge
{
int v, w, next;
} edge[mxn * 4];
int head[mxn];
int k = 0;
struct Node
{
int x, dis;
bool operator < (const Node a) const
{
return dis < a.dis;
}
};
void Add(int u, int v, int w)
{
edge[++ k] = (Edge){ v, w, head[u] };
head[u] = k;
}
int dis[mxn], use[mxn];
void Spfa(int s)
{
for(int i = 0; i <= n; i ++)
dis[i] = -INF, use[i] = 0;
dis[s] = 0;
priority_queue<Node> q;
q.push((Node){ s, dis[s] });
int u, v, w;
while(! q.empty())
{
u = q.top().x; q.pop();
use[u] = 0;
for(int i = head[u]; i; i = edge[i].next)
{
v = edge[i].v;
w = edge[i].w;
if(dis[v] < dis[u] + w)
{
dis[v] = dis[u] + w;
if(! use[v])
{
q.push((Node){ v, dis[v] });
use[v] = 1;
}
}
}
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &ar[i]);
for(int i = 2; i <= n; i ++)
{
if(ar[i] == ar[i - 1])
{
Add(i-1, i, 0);
Add(i, i-1, 0);
}
else if(ar[i] > ar[i - 1])
{
Add(i-1, i, 1);
}
else
{
Add(i, i-1, 1);
}
}
for(int i = 1; i <= n; i ++)
{
Add(0, i, 1);
}
Spfa(0);
ll sum = 0;
for(int i = 1; i <= n; i ++)
sum += dis[i];
printf("%lld\n", sum);
return 0;
}
P6365 众数出现的次数
思路
- 分析:对于这一题给出的两张牌我们可以稍作改变,第一张的值不变,第二张的值变为 第一张的值疑惑第二章的值 的结果,这样在用 map统计 每个人手中牌的的数字出现的次数,如果两张牌数字相同,只统计一次,,,
代码
#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define db double
#define INF 0x3f3f3f3f
const int mxn = 1e6 + 10;
int ar[mxn], br[mxn];
int n;
map<int, int> mp;
int main()
{
scanf("%d", &n);
int x, y;
for(int i = 1; i <= n; i ++)
{
scanf("%d %d", &x, &y);
ar[i] = x;
br[i] = x^y;
mp[ar[i]] ++;
if(ar[i] != br[i])
mp[br[i]] ++;
}
int mx = -1, val;
for(auto x : mp)
{
if(mx < x.second)
{
mx = x.second;
val = x.first;
}
}
printf("%d\n", val);
return 0;
}
P6366 特殊的翻转
思路
- 先把所给的数转化为二进制(直接看代码部分)
- 这属于开关问题,有一些东西要注意一下,对于某个要反转的位置如果 我们反转两次以上是没有意义的,其次对于 各个 翻转操作的顺序,谁在前,谁在后并不影响结果。
- 对这一题如果我们不考虑开头和结尾的特殊情况(可以反转两个,不必反转三个),那么对于中间的要反转的情况,那么我们都是一下反转相邻的三位,对于中间位置从左向右开始我们一旦遇到1,说明就
必须
要将这个位置及以后的2位都进行反转(不反转的化,是不符合要求的),这样问题的规模就减小了1,然后依次向右遍历 遇见1就反转就行了
- 现在我们考虑开头的两种情况:反转开头2位 、反转开头连续的3位,我们对两种情况分别进行,尝试翻转就行了,显然这两种情况都有可能反转出符合题意的状态,但是可能的反转的次数不一样,这个时候我们去反转的次数小的那个数就行了,如果只有一个能反转出全0状态,那就输出那个结果,如果都不能反转出全0状体,输出 No
- 对于结尾也是于开头,一样可以反转两位或者反转三位,这个时候我们操作是在所转化的字符串末尾 补个0,这样就避免了 这两种情况的选择的烦恼
代码
#include
#include
#include
#include
#include
#include
using namespace std;
const int mxn = 1e6 + 10;
map<char, string> mp, tr;
char ar[mxn];
int br[mxn << 2];
int main()
{
mp['0'] = "0000"; tr['0'] = "0000";
mp['1'] = "0001"; tr['1'] = "1000";
mp['2'] = "0010"; tr['2'] = "0100";
mp['3'] = "0011"; tr['3'] = "1100";
mp['4'] = "0100"; tr['4'] = "0010";
mp['5'] = "0101"; tr['5'] = "1010";
mp['6'] = "0110"; tr['6'] = "0110";
mp['7'] = "0111"; tr['7'] = "1110";
mp['8'] = "1000"; tr['8'] = "0001";
mp['9'] = "1001"; tr['9'] = "1001";
mp['A'] = "1010"; tr['A'] = "1010";
mp['B'] = "1011"; tr['B'] = "1101";
mp['C'] = "1100"; tr['C'] = "0011";
mp['D'] = "1101"; tr['D'] = "1011";
mp['E'] = "1110"; tr['E'] = "0111";
mp['F'] = "1111"; tr['F'] = "1111";
scanf("%s", ar);
string s;
int n = strlen(ar);
s += "0";
for(int i = 0; i < n; i ++)
s += mp[ar[i]];
s += "0";
int l = 0, r = s.size() - 1;
while(s[l] == '0')
l ++;
s[l - 1] = '1';
string ss = s;
int ans1 = 0;
for(int i = l; i < r; i ++)
{
if(s[i - 1] == '1')
{
s[i - 1] = '0';
ans1 ++;
if(s[i] == '1')
s[i] = '0';
else
s[i] = '1';
if(s[i+1] == '1')
s[i+1] = '0';
else
s[i+1] = '1';
}
}
for(int i = l; i < r; i ++)
if(s[i] == '1')
{
ans1 = -1;
break;
}
int ans2 = 0;
for(int i = l + 2; i <= r; i ++)
{
if(ss[i - 2] == '1')
{
ss[i - 2] = '0';
ans2 ++;
if(ss[i - 1] == '1')
ss[i - 1] = '0';
else
ss[i - 1] = '1';
if(ss[i] == '1')
ss[i] = '0';
else
ss[i] = '1';
}
}
for(int i = l; i < r; i ++)
{
if(ss[i] == '1')
{
ans2 = -1;
break;
}
}
if(ans1 == -1 && ans2 == -1)
printf("No\n");
else if(ans1 == -1)
printf("%d\n", ans2);
else if(ans2 == -1)
printf("%d\n", ans1);
else
printf("%d\n", min(ans1, ans2));
return 0;
}
- 注意 分类讨论,与补0操作,使复杂的操作变的简单些