以下代码均没有经过数据验证,若有不正确的地方,劳烦各位dalao指出
Problem A:Open Box
题目大意:
给一把锁,锁的密码是9394,输入4个字符表示该锁每一位密码现在所在的位置,问最少需要多少次操作可以到9394,密码锁是旋转的0-9成环。
算法思路:
水题,模拟一下操作可以往后转或者往前转,选最优的就好了。
/*
@resources: GDCPC 2017 A
@date: 2017-05-09
@author: QuanQqqqq
@algorithm: yy
*/
#include <bits/stdc++.h>
using namespace std;
int code[] = {9,3,9,4};
int change(int a,int b){
int t1,t2;
if(a > b){
t1 = a - b;
t2 = b + 10 - a;
} else {
t1 = b - a;
t2 = a + 10 - b;
}
return min(t1,t2);
}
int main(){
char ch[10];
int T,ans,cas = 1;
scanf("%d",&T);
getchar();
while(T--){
gets(ch);
ans = 0;
for(int i = 0;i < 4;i++){
ans += change(ch[i] - '0',code[i]);
}
printf("Case #%d: %d\n",cas++,ans);
}
}
Problem B:Power Grid
题目大意:
有n个城市,m条路径,每一个城市每到达一次可以提供ei点电量,每次从wi城市到vi城市需要花费wi点电量,两个城市之间会有多种路线,现在问你任意选取一个城市作起始点,他会提供给你该城市所提供的ei点电量,问是否可以访问完所有城市。城市可以重复访问。
算法思路:
题目好像没有说,自己可以访问自己花费多少电力(我们假设这种情况存在。。)。
首先需要访问完所有城市,那么所有城市一定要连通,如果不连通,则输出NO。
城市之间可以多次访问,在联通的条件下,如果经过某些城市之后回到该点,所拥有的电量增加,则说明一定可以跑完整幅图(因为你只要一直充电。。。然后等到足够电的时候开始跑图就好了)
这种情况需要用SPFA来判一下负环,如果花费的路径出现负环的话,则说明一定可以跑完整幅图,则输出YES。
以上特殊情况都不存在的话,先跑一个整幅图的最短路,然后,对于每一个点都初始化他的Ei点电量,然后开始简单状压DP,其他点都初始化成-1,对于每一个点有
如果满足dp[s][i] - g[i][j] >= 0
则有dp[s ^ (1 << (j - 1))][j] = max(dp[s ^ (1 << (j - 1))][j],dp[s][i] - g[i][j] + e[i]);
然后dp完毕后,再看dp[0][1 -> n]这些点,有没有存在大于等于0的即可,存在则为YES,不存在为NO。
/*
@resources: GDCPC 2017 B
@date: 2017-05-11
@author: QuanQqqqq
@algorithm: dp spfa
*/
#include <bits/stdc++.h>
#define ll long long
#define MAXN 20
using namespace std;
const ll INF = (1LL << 33);
ll dp[1 << 17][MAXN];
ll e[MAXN];
ll mapp[MAXN][MAXN];
ll d[MAXN];
ll g[MAXN][MAXN];
ll in[MAXN];
bool mark[MAXN];
bool flag[MAXN];
int n;
queue<int> q;
void init(){
memset(dp,-1,sizeof(dp));
memset(flag,false,sizeof(flag));
for(int i = 0;i < MAXN;i++){
for(int j = 0;j < MAXN;j++){
if(i == j){
mapp[i][j] = 0;
g[i][j] = 0;
} else {
g[i][j] = INF;
mapp[i][j] = INF;
}
}
}
}
void dfs(int v){
mark[v] = true;
for(int i = 1;i <= n;i++){
if(!mark[i]){
dfs(i);
}
}
}
bool spfa(int u){
memset(mark,false,sizeof(mark));
memset(in,0,sizeof(in));
for(int i = 0;i < MAXN;i++){
d[i] = INF;
}
while(!q.empty()){
q.pop();
}
int v;
d[u] = 0;
mark[u] = true;
q.push(u);
while(!q.empty()){
v = q.front();
mark[v] = false;
for(int i = 1;i <= n;i++){
if(d[v] + mapp[v][i] < d[i] && i != v){
d[i] = d[v] + mapp[v][i];
if(in[v] >= 1){
d[i] -= e[v];
}
if(!mark[i]){
in[i]++;
if(in[i] > n){
return true;
}
mark[i] = true;
q.push(i);
}
}
}
q.pop();
}
for(int i = 1;i <= n;i++){
g[u][i] = d[i];
}
return false;
}
int main(){
int T,u,v,m,cas = 1;
ll w;
bool con,ans;
scanf("%d",&T);
while(T--){
init();
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;i++){
scanf("%lld",&e[i]);
}
ans = con = false;
for(int i = 1;i <= m;i++){
scanf("%d %d %lld",&u,&v,&w);
u++,v++;
if(v == u){ //该点无限充电
if(w < e[v]){
flag[v] = true;
}
}
mapp[u][v] = min(mapp[u][v],w);
}
printf("Case #%d: ",cas++);
for(int i = 1;i <= n;i++){
memset(mark,0,sizeof(mark));
dfs(i);
int total = 0;
for(int j = 1;j <= n;j++){
if(mark[i]){
total++;
}
}
if(total == n){
con = true;
if(flag[i]){
ans = true;
}
}
}
if(!con){ //不连通
printf("No\n");
continue;
}
if(ans){ //某点无限充电,且该点可以遍历整个图
printf("Yes\n");
continue;
}
for(int i = 1;i <= n;i++){
if(spfa(i)){
ans = true;
break;
}
}
if(ans){ //存在负环
printf("Yes\n");
continue;
}
for(int i = 1;i <= n;i++){
dp[((1 << n) - 1) ^ (1 << (i - 1))][i] = e[i];
}
for(int s = (1 << n) - 2;s >= 0;s--){
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
if(s & (1 << (j - 1))){
if(dp[s][i] - g[i][j] >= 0){
dp[s ^ (1 << (j - 1))][j] = max(dp[s ^ (1 << (j - 1))][j],dp[s][i] - g[i][j] + e[i]);
}
}
}
}
}
for(int i = 1;i <= n;i++){
if(dp[0][i] >= 0){
ans = true;
}
}
if(ans){
printf("Yes\n");
} else {
printf("No\n");
}
}
}
/*
3
3 2
5 3 3
0 1 5
1 2 3
2 1
10 0
0 1 20
4 5
10 5 0 0
0 1 20
0 1 5
1 2 5
2 1 5
1 3 5
*/
Problem C:Stockpile
题目大意:
有两种分段函数,一种是对应PUT,一种对应CALL。
先输入三个数,Ci,Pi,Ki,分别对应某函数
PUT的时候
对于P > X的情况,有函数Y = (P - X) * K - C
对于P <= X的情况,有函数Y = -C
CALL的时候
对于X > P的情况,有函数Y = (X - P) * K - C
对于X <= P的情况,有函数Y = -C
现在需要你将所有函数合并变成分段函数。
总函数的条数为10^6
测试数据为2000组
算法思路:
其实计算是O(n)的时间复杂度对这题也是超时的。O(n*T)。。。
赛场上没看题。
对于这种分段,其实每一个分段点就只有P这个点,将所有P点弄成集合,sort一下离散化去重点。
接下来个人做法有点复杂,因为很明显对于每一个函数,你每次需要维护这个函数的a和b。所以只需要对两颗树状数组进行操作,每次更新整段区间的值即可。
赛后队友YY了一下这题为何是简单题。。当P点已经排好序的情况下,其实可以只维护两个前缀和,每次到达相应的P点,就更新该P点所拥有的函数的a和b,直接打印即可。。。
时间复杂度均为O(nlogn)
/*
@resources: GDCPC 2017 C
@date: 2017-05-08
@author: QuanQqqqq
@algorithm: 树状数组
*/
#include <bits/stdc++.h>
#define ll long long
#define MAXN 1000005
#define lowbit(i) i & (-i)
using namespace std;
struct line{
ll a,b;
};
struct node{
ll c,p,k;
char ch[10];
}num[MAXN];
ll bits[2][MAXN];
ll pt[MAXN];
void add(ll i,ll v,ll p){
while(i < MAXN){
bits[p][i] += v;
i += lowbit(i);
}
}
ll query(ll i,ll p){
ll ans = 0;
while(i){
ans += bits[p][i];
i -= lowbit(i);
}
return ans;
}
int main(){
int T;
ll a,b,n;
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
getchar();
ll t = 0;
memset(bits,0,sizeof(bits));
for(ll i = 0;i < n;i++){
scanf("%s %lld %lld %lld",num[i].ch,&num[i].c,&num[i].p,&num[i].k);
getchar();
pt[++t] = num[i].p;
}
pt[++t] = 0;
sort(pt + 1,pt + t + 1);
ll l = 1;
for(ll i = 2;i <= t;i++){
if(pt[i] != pt[l]){
pt[++l] = pt[i];
}
}
for(ll i = 0;i < n;i++){
ll tmp = lower_bound(pt + 1,pt + 1 + l,num[i].p) - pt;
if(num[i].ch[0] == 'C'){
b = -num[i].p * num[i].k - num[i].c;
add(tmp + 1,b,0);
add(tmp + 1,num[i].k,1);
add(1,-num[i].c,0);
add(tmp + 1,num[i].c,0);
} else {
b = num[i].p * num[i].k - num[i].c;
add(tmp + 1,-b,0);
add(1,b,0);
add(tmp + 1,num[i].k,1);
add(1,-num[i].k,1);
add(tmp + 1,-num[i].c,0);
}
}
for(ll i = 2;i <= l;i++){
a = query(i,1);
b = query(i,0);
if(a == 0){
printf("[%lld,%lld] b=%lld\n",pt[i - 1],pt[i],b);
} else {
if(b < 0){
printf("[%lld,%lld] b=%lld*x%lld\n",pt[i - 1],pt[i],a,b);
} else {
printf("[%lld,%lld] b=%lld*x+%lld\n",pt[i - 1],pt[i],a,b);
}
}
}
a = query(MAXN,1);
b = query(MAXN,0);
if(a == 0){
printf("[%lld,...] b=%lld*x+%lld\n",pt[l],a,b);
} else {
if(b < 0){
printf("[%lld,...] b=%lld*x%lld\n",pt[l],a,b);
} else {
printf("[%lld,...] b=%lld*x+%lld\n",pt[l],a,b);
}
}
}
}
/*
2
2
Call 20 30 10
Put 10 20 5
4
Call 32714 170443 16
Call 6846 101607 5
Call 1827 60167 31
Call 1909 124978 47
*/
Problem D:Boggle Game
题目大意:
还没读题,待补。。听说用来防AK的。。
算法思路:
//等待解题
Problem E: Dungeons & Dragons
题目大意:
有一个怪物有b滴血,你有两个方程分别是:
攻击时间:s + i * x
攻击伤害:w + i * y
i代表你的等级,vMax代表你的最高可到达的等级,s,w,x,y均为定值。
所有数据均不大于10^9次方
算法思路:
需要造成伤害血量=攻击次数*攻击伤害
有一个值一定是小于sqrt(b)的。所以只要枚举这两个值到sqrt(b)即可。
b最大为10^9所以,枚举到50000即可。
题目没有说攻击伤害会有0的情况。。
/*
@resources: GDCPC 2017 E
@date: 2017-5-8
@author: QuanQqqqq
@algorithm: 枚举
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll s,w,x,y,vMax,b;
ll getRankTime(ll i){
ll time = (ll)ceil(b * 1.0 / (w + i * y));
return time * (s + i * x);
}
ll getAttackTime(ll time){
ll i = (ll)ceil((b * 1.0 / time - w) / y);
return time * (s + i * x);
}
int main(){
int T,cas = 1;
scanf("%d",&T);
while(T--){
scanf("%lld %lld %lld %lld %lld %lld",&s,&w,&x,&y,&vMax,&b);
ll ans = getRankTime(0);
ll v = min(50000LL,vMax);
for(ll i = 1;i <= v;i++){
ans = min(ans,getRankTime(i));
}
for(ll i = 1;i <= 50000LL;i++){
ans = min(ans,getAttackTime(i));
}
printf("Case #%d: %lld\n",cas++,ans);
}
}
/*
2
2 2 2 2 8 10
2 2 1 2 8 10
*/
Problem F: Werewolves
题目大意:
有一大个屋子,现在需要知道这个屋子里有多少个房,且找出某一个房间到所有房间的距离(取所有距离的最大值),最小的房间。0为门或者路,1可以构成墙或者门的边边。
对于房间的门有以下定义:
门只能是2 * 5,2 * 4,4 * 2,5 * 2如下的格式
11 11 100001 1000001
00 00 100001 1000001
00 00
00 00
00 00
00 00
11
对于一个门,他有如下定义:
他旁边一定要有墙,且门两侧需要有路。对于每一个房间,必须要有拐角,即房间的长或者宽不能跟房间的长或者宽长得一样。
算法思路:
就。。一直dfs房间,dfs墙。。。。然后搞完之后再合并一下路径打出每个房间之间的路径,加一个floyd找最短路径的房间。。。很麻烦。。有空补。
//等待解题
Problem G: Scythe
题目大意:
给一棵树,需要你使得这棵树需要满足给定的最大深度和最小深度。
算法思路:
树形dp搞一搞。O(n^2)
//等待解题
Problem H: Codename
题目大意:
有n个女生和m个男生,每一个女生喜欢ai个男生,如果该男生被所有女生都喜欢,这个男生被叫做famous guys,现在问有多少个famous guys。
算法思路:
简单题,求最大覆盖,对于每一个女生喜欢ai个男生,那么她一定不喜欢m - ai个男生,这些不被喜欢的男生数肯定不是famous guy,再找找m个里有多少个是famous guy就可以了,注意判小于0的情况。
/*
@resources: GDCPC 2017 H
@date: 2017-05-09
@author: QuanQqqqq
@algorithm: yy
*/
#include <bits/stdc++.h>
#define ll long long
#define MAXN 2000005
using namespace std;
int main(){
int T,cas = 1;
scanf("%d",&T);
ll n,m,tmp,ans;
while(T--){
scanf("%lld %lld",&n,&m);
ans = m;
for(ll i = 0;i < n;i++){
scanf("%lld",&tmp);
ans -= (m - tmp);
}
if(ans < 0){
ans = 0;
}
printf("Case #%d: %lld\n",cas++,ans);
}
}
Problem I: Betrayal at House on the Hill
题目大意:
给n个坐标,现在问这要使得这n个点都联通需要多长路径
算法思路:
最小生成树搞一发,详情请看红书P168页。
改改板样例过了。。
/*
@resources: GDCPC 2017 I
@date: 2017-05-10
@author: QuanQqqqq
@algorithm: yy
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 111111;
const int INF = 0x3f3f3f3f;
inline int lowbit(const int &x){
return x & (-x);
}
struct Edge{
int u,v;
double c;
Edge(int _u,int _v,double _c) : u(_u),v(_v),c(_c){}
Edge(){}
}edge[MAXN * 4];
inline bool operator < (const Edge &a,const Edge &b){
return a.c < b.c;
}
struct Node{
int key,id;
Node(int _k = 0,int _i = 0) : key(_k),id(_i){}
}Tree[MAXN];
inline bool operator < (const Node &a,const Node &b){
return a.key < b.key;
}
int IDx[MAXN],IDy[MAXN],bak[MAXN];
int id[MAXN],father[MAXN],x[MAXN],y[MAXN];
int find(const int &x){
return father[x] == x ? x : father[x] = find(father[x]);
}
inline bool cmp1(const int &i,const int &j){
return x[i] - y[i] > x[j] - y[j] || x[i] - y[i] == x[j] - y[j] && y[i] > y[j];
}
inline bool cmp2(const int &i,const int &j){
return x[i] - y[i] < x[j] - y[j] || x[i] - y[i] == x[j] - y[j] && y[i] > y[j];
}
inline bool cmp3(const int &i,const int &j){
return x[i] + y[i] < x[j] + y[j] || x[i] + y[i] == x[j] + y[j] && y[i] > y[j];
}
inline bool cmp4(const int &i,const int &j){
return x[i] + y[i] < x[j] < y[j] || x[i] + y[i] == x[j] + y[j] && y[i] < y[j];
}
inline void Process(int x[],int idx[],int n){
for(int i = 0;i < n;i++){
bak[i] = x[i];
}
sort(bak,bak + n,greater<int>());
int p = unique(bak,bak + n) - bak;
for(int i = 0;i < n;i++){
idx[i] = lower_bound(bak,bak + p,x[i],greater<int>()) - bak + 1;
}
}
inline void add_edge(int &N,const int &u,const int &v){
edge[N++] = Edge(u,v,sqrt(pow(x[u] - x[v],2) + pow(y[u] - y[v],2)));
}
inline int get_min(const int &p){
Node tmp(INF);
for(int i = p;i;i ^= lowbit(i)){
if(Tree[i].id != -1){
tmp = min(tmp,Tree[i]);
}
}
return tmp.key == INF ? -1 : tmp.id;
}
inline void insert(const int &n,const int &p,const Node &it){
for(int i = p;i <= n;i += lowbit(i)){
if(Tree[i].id == -1 || it < Tree[i]){
Tree[i] = it;
}
}
}
inline double MinimumManhattanSpaningTree(int x[],int y[],int n){
Process(x,IDx,n);
Process(y,IDy,n);
int N = 0;
for(int i = 0;i < n;++i){
id[i] = i;
}
sort(id,id + n,cmp1);
for(int i = 1;i <= n;++i){
Tree[i].id = -1;
}
for(int i = 0;i < n;++i){
int u = id[i],v = get_min(IDy[u]);
if(v != -1){
add_edge(N,u,v);
}
insert(n,IDy[u],Node(x[u] + y[u],u));
}
for(int i = 0;i < n;++i){
id[i] = i;
}
sort(id,id + n,cmp2);
for(int i = 1;i <= n;++i){
Tree[i].id = -1;
}
for(int i = 0;i < n;++i){
int u = id[i],v = get_min(IDx[u]);
if(v != -1){
add_edge(N,u,v);
}
insert(n,IDx[u],Node(x[u] + y[u],u));
}
for(int i = 0;i < n;++i){
id[i] = i;
}
sort(id,id + n,cmp3);
for(int i = 1;i <= n;++i){
Tree[i].id = -1;
}
for(int i = 0;i < n;++i){
int u = id[i],v = get_min(IDy[u]);
if(v != -1){
add_edge(N,u,v);
}
insert(n,IDy[u],Node(-x[u] + y[u],u));
}
for(int i = 0;i < n;++i){
id[i] = i;
}
for(int i = 1;i <= n;++i){
Tree[i].id = -1;
}
for(int i = 0;i < n;++i){
int u = id[i],v = get_min(IDx[u]);
if(v != -1){
add_edge(N,u,v);
}
insert(n,IDx[u],Node(x[u] - y[u],u));
}
sort(edge,edge + N);
for(int i = 0;i < n;++i){
father[i] = i;
}
double res = 0;
for(int i = 0;i < N;++i){
int u = find(edge[i].u), v = find(edge[i].v);
if(u != v){
father[u] = v;
res += edge[i].c;
}
}
return res;
}
int main(){
int T,cas = 1,n;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i = 0;i < n;i++){
scanf("%d %d",&x[i],&y[i]);
}
printf("Case #%d: %.2lf\n",cas++,MinimumManhattanSpaningTree(x,y,n));
}
}
/*
1
4
0 0
0 1
1 0
2 2
*/
Problem J: Carcassonne
题目大意:
先有a(0,0),b1(1,0)这两个点,先a绕着b1旋转转出b2,然后a绕着b2转转出b2….一直旋转下去。。
然后从a点出发,跟着线走,现在问第n个点的坐标在哪里
算法思路:
比赛后期读的题,2分钟想到大概思路。。结果卡住了。。
对于一个n点,每次先找到比他大的2^k最小值,那么他一定绕着2^(k - 1)旋转,把这个点保存下来
然后位置回溯到n1 = 2^k - n
然后n1也继续找一个2^k1比他大的最小值。。。一直循环下去
直到ni为2^k结束,然后用这个ni这个点开始转回去。
由于这个点是第2^k个点,打一个40不到的表即可满足题目10^18的n这个数据。
然后需要一个旋转公式搞一搞。。
/*
@resources: GDCPC 2017 J
@date: 2017-5-8
@author: QuanQqqqq
@algorithm: yy
*/
#include <bits/stdc++.h>
#define ll long long
#define MAXN 100
using namespace std;
struct point{
ll x,y;
point(ll _x,ll _y) : x(_x),y(_y){}
point(){}
};
point p[MAXN];
vector<ll> vs;
point xuanzhuan(point a,point b){
point ans;
ans.x = b.x + a.y - b.y;
ans.y = b.x - a.x + b.y;
return ans;
}
ll find(ll n){
ll ans = 1;
while(ans < n){
ans <<= 1LL;
}
return ans;
}
bool check(ll n){
ll total = 0;
while(n){
if(n & 1){
total++;
}
n >>= 1;
}
return total <= 1;
}
ll getLen(ll n){
ll ans = 0;
while(n){
n >>= 1;
ans++;
}
return ans;
}
void init(){
p[0] = point(0,0);
p[1] = point(1,0);
for(ll i = 2;i < MAXN;i++){
p[i] = xuanzhuan(point(0,0),p[i - 1]);
}
}
point getAns(ll n){
point ans = p[getLen(n)];
ll len = vs.size();
for(ll i = len - 1;i >= 0;i--){
ans = xuanzhuan(ans,p[vs[i]]);
}
vs.clear();
return ans;
}
int main(){
int T,cas = 1;
ll n;
point ans;
scanf("%d",&T);
while(T--){
init();
scanf("%lld",&n);
while(n){
ll t = find(n) - n;
if(t == 0){
ans = getAns(n);
break;
} else {
vs.push_back(getLen(find(n) / 2));
n = find(n) - n;
}
}
printf("Case #%d: (%lld,%lld)\n",cas++,ans.x,ans.y);
}
}
Problem K:Coup
题目大意:
还没读题,待补。。
算法思路:
//等待解题
Problem L:Advertising
题目大意:
一个XML需要渲染,对于每一个url,他有n条链接
可以是一个id链接到另一个url,或者一个id结束。
现在题目需要你从第一个url开始遍历,找出所有inline的id
可以重复打印。。忘记有没有回环了。。(题目并没有讲)
算法思路:
用一个map加一个vector存路径当邻接表,然后dfs遍历一次有向图,遇到inline的就输出就可以了。
/*
@resources: GDCPC 2017 L
@date: 2017-5-8
@author: QuanQqqqq
@algorithm: dfs
*/
#include <bits/stdc++.h>
#define ll long long
#define MAXN 3005
using namespace std;
map<string,vector<string> >mapp;
string root[MAXN];
map<string,bool> mapb;
bool flag;
void dfs(string str){
if(mapb[str]){
if(flag){
cout << " ";
}
flag = true;
cout << str;
return ;
}
int len = mapp[str].size();
for(int i = 0;i < len;i++){
dfs(mapp[str][i]);
}
}
int main(){
int T,cas = 1;
int n,t;
string str1,str2,str3;
scanf("%d",&T);
while(T--){
cin >> n;
getchar();
flag = false;
mapb.clear();
mapp.clear();
for(int i = 0;i < n;i++){
cin >> root[i] >> t;
getchar();
for(int j = 0;j < t;j++){
cin >> str1 >> str2;
if(str2 == "inline"){
mapb[str1] = true;
mapp[root[i]].push_back(str1);
} else {
cin >> str3;
mapp[root[i]].push_back(str3);
}
}
}
printf("Case #%d: ",cas++);
dfs(root[0]);
cout << endl;
}
}
/*
2
2
http://demo.ad.com/vast1 2
id1 wrapper http://demo.ad.com/vast2
id2 inline
http://demo.ad.com/vast2 1
id3 inline
4
http://demo.ad.com/vast1 2
id1 wrapper http://demo.ad.com/vast2
id2 wrapper http://demo.ad.com/vast3
http://demo.ad.com/vast2 1
id3 wrapper http://demo.ad.com/vast4
http://demo.ad.com/vast3 2
id4 inline
id5 inline
http://demo.ad.com/vast4 1
id6 inline
*/