JNU周练1013
给出n、m(n<=m)和m个数字,在m个数字中选n个数字,令这n个数字中最大数与最小数值差最小。
数据量小,直接暴力
#include <algorithm>
using namespace std;
int main()
{
int a[60], n, m;
cin >> n >> m;
for(int i = 0; i < m; i++)
cin >> a[i];
sort(a, a+m);
int min = 1005;
for(int i=0; i<=m-n; i++)
{
if(a[i+n-1] - a[i] < min)
min = a[i+n-1] - a[i];
}
cout << min << endl;
return 0;
}
给出屏幕与电影的宽高比,电影按比例缩放,尽量占满屏幕,求屏幕没被占的面积的比例。输出要求:分子分母互质。
找规律,无论是电影适应屏幕还是屏幕适应电影,得出的比例都是一样的。同时注意两比例相同时输出0/1。
using namespace std;
int gcd(int x, int y)
{
int r;
while(y!=0)
{
r = x%y;
x = y;
y = r;
}
return x;
}
int main()
{
int a, b, c, d, temp;
cin >> a >> b >> c >> d;
a = a * d;
c = c * b;
if(a < c)
{
temp = a;
a = c;
c = temp;
}
c = a - c;
temp = gcd(a,c);
a /= temp;
c /= temp;
cout << c << "/" << a << endl;
return 0;
}
题意:
一人去参加比赛。每答对一题得一分。连续答对k题时先加一分然后总分数乘2然后连续答对题数归0。
然后给你题目总数n和他答对题目的个数m问你他能得的最低分是多少。
要获得最低分,要尽量避免连续答对k题,或者让连续答对k题的情况尽量出现在分数少的时候
方法是:设答对为1,答错为0,以k-1个"1"和1个"0"为一组从后往前排列,
最后得出两部分,一部分是x组k-1个"1"和1个"0",另一部分为连续y个1
则n = k * x + y; m = (k-1) * x + y; ---> 得 x = n - m
第一部分好求:(k-1) * x
第二部分需要找规律:
另z = y / k,则有z*k次答对,取z = 1, 2, 3,...总分数如下
那么分数变化为:2*k,(2*k+k)*2,((2*k+k)*2+k)*2......。
抽象出来这就是一个数列递推公式为:a[z]=2*(a[z-1]+k)。
展开移项后得:a[z]+2*k=2*(a[z-1]+2*k)。设c[z]=a[z]+2*k。那么c[z]/c[z-1]=2。等比数列。
而c[0]=a[0]+2*k=2k。那么c[z]=2*k*2^z。所以a[z]=k*2^(z+1)-2*k。
数据大,要用到快速幂
*/
注意中间数大,要开long long
using namespace std;
const long long MOD = 1000000009;
//计算a^bmodn
long long modexp_recursion(long long a,long long b,long long n)
{
long long t = 1;
if (b == 0)
return 1;
if (b == 1)
return a%n;
t = modexp_recursion(a, b>>1, n);
t = t*t % n;
if (b&0x1)
{
t = t*a % n;
}
return t;
}
int main()
{
long long n, m, k;
cin >> n >> m >> k;
long long a = m / (k-1);
if(a <= n - m)
cout << m << endl;
else
{
long long ret = 0;
a = n - (n-m)*k;
long long b = a / k;
if(b>0)
{
//cout << "b:" << b << endl;
//cout << modexp_recursion(2, b, MOD) << endl;
ret = 2 * k * modexp_recursion(2, b, MOD) - 2*k;
ret = ret % MOD;
//cout << "ret:" << ret << endl;
}
ret = (ret + (n-m) * (k-1)) % MOD;
ret = (ret + (a-a/k*k)) % MOD;
cout << ret << endl;
//cout << "aaa:" << modexp_recursion(2,100,MOD) << endl;
}
return 0;
}
CodeForces 337D
题意:给一棵n个结点的树,任意两个节点的距离是指连接两点的最短的边数
在树上的某个结点有一个“恶魔之书”,这本书会让距离它d以内的节点都受到影响已知有m个节点收到了影响,问最多有几个结点可能放着“恶魔之书”?
据说是树形dp,看题解知道大概思路后,自己写代码,挑了无数bug过了。。
规定点1为根节点
dp[i][0] --- 代表节点 i 子树中距 i 最远的受影响点和 i 的距离。
dp[i][1] --- 上述的次远距离
dp[i][2] --- 代表节点 i 子树以外的部分 距 i 最远的受影响点和 i 的距离。
若某点dp[i][0] 和 dp[i][2]均小于或等于影响范围d,表示该点满足条件。
dp[i][0] 和 dp[i][1]好求,dp[i][2] 则要综合(1) i 的祖先节点和(2)其父节点的其他子树的情况。
状态转移方程:
u(父)--- v(子)
fa(父) --- u, i (子)
dp[u][0] = max(dp[v][0] + 1)
dp[u][2] = max(dp[fa][2], {dp[fa][0] or dp[fa][1]} + 1)
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
#define M 200010
int head[N], vid[N], vis[N], eNum, n, m, d;
struct Edge
{
int e;
int next;
}edge[M];
int dp[N][3];
void AddEdge(int u, int v)
{
edge[eNum].e = v;
edge[eNum].next = head[u];
head[u] = eNum++;
}
void init()
{
int temp, u, v;
eNum = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
memset(dp, -1, sizeof(dp));
cin >> n >> m >> d;
for(int i=1; i<=m; i++)
{
cin >> temp;
vis[temp] = 1;
}
for(int i=1; i<n; i++)
{
cin >> u >> v;
AddEdge(u, v);
AddEdge(v, u);
}
}
void dfs1(int u, int fa)
{
if(vis[u]==1) dp[u][0] = 0;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].e;
if(v == fa) continue;
dfs1(v, u);
if(dp[v][0] + 1 > dp[u][0])
{
if(dp[v][0]!=-1)
{
dp[u][1] = dp[u][0];
dp[u][0] = dp[v][0] + 1;
}
}
else if(dp[v][0] + 1 > dp[u][1] && dp[v][0] != -1)
{
dp[u][1] = dp[v][0] + 1;
}
}
}
void dfs2(int u, int fa)
{
if(vis[u]==1 && dp[u][2]==-1) dp[u][2] = 0;
for(int i=head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].e;
if(v == fa) continue;
if(dp[u][2]!=-1) dp[v][2] = dp[u][2] + 1;
if(dp[v][0]+1 == dp[u][0])
{
if(dp[u][1] != -1)
dp[v][2] = max(dp[v][2], dp[u][1]+1);
}
else if(dp[u][0]!=-1)
dp[v][2] = max(dp[v][2], dp[u][0]+1);
dfs2(v, u);
}
}
int main()
{
init();
dfs1(1, 0);
dfs2(1, 0);
int ans = 0;
for(int i=1; i<=n; i++)
if(dp[i][0]<=d && dp[i][2]<=d)
ans++;
//for(int i=1; i<=n; i++)
//{
// cout << dp[i][0] << " " << dp[i][1] << " " << dp[i][2] << endl;
//}
cout << ans << endl;
return 0;
}
/*
9 4 3
1 2 8 9
1 5
5 4
4 3
3 2
5 6
6 7
7 8
8 9
*/
题意,给你一颗n个数,构建一颗树,包含所有数,满足根结点等于儿子结点的乘积,并且所有叶子结点都是素数。
关键点:对每个a[i]分解质因子,得出因子个数。在此基础上建树,由于n<=8,所以直接dfs枚举所有情况,得出最小的结点数。
#include <algorithm>
#include <cstring>
using namespace std;
struct Num
{
long long b;
int c;
}num[10];
int n; //存输入
int a[80000], index;
long long leftt[10];//标记剩余多少
int vis[10], sum, ans_min;
int prime(int x)
{
int flag = 0;
for(int i=2; i*i<=x; i++)
{
if(x%i==0)
{
flag = 1;
break;
}
}
if(flag)
return 0;
return 1;
}
//筛选法求1000000内素数
int prime1(int x)
{
int flag = 0;
for(int i=0; a[i]*a[i]<=x; i++)
{
if(x%a[i]==0)
{
flag = 1;
break;
}
}
if(flag)
return 0;
return 1;
}
bool cmp(Num a, Num b)
{
return a.c < b.c;
}
void dfs(int t)
{
//cout << "t: " << t << endl;
if(t == n)
{
int ret = sum, retn = n;
for(int i=1; i<=n; i++)
{
if(vis[i]!=0)
{
ret -= num[i].c;
retn --;
}
}
if(retn>1) ret++;
if(ret < ans_min) ans_min = ret;
//------test
//if(ret==8)
//{
// for(int i=1; i<=n; i++)
// {
// cout << vis[i]<<endl;
// }
//}
return ;
}
for(int i=t+1; i<=n; i++)
{
long long temp = leftt[i];
if(leftt[i] >= num[t].b && leftt[i]%num[t].b==0)
{
leftt[i] /= num[t].b;
vis[t] = i;
}
dfs(t+1);
if(temp!=leftt[i])
{
leftt[i] *= num[t].b;
vis[t] = 0;
}
}
}
int main()
{
long long flag;
index = 0;
for(int i=2; i<=1000; i++)
{
if(prime(i))
a[index++] = i;
}
for(int i=1001; i<=1000000; i++)
{
if(prime1(i))
a[index++] = i;
}
cin >> n;
for(int i=1; i<=n; i++)
cin >> num[i].b;
for(int i=1; i<=n; i++)
{
num[i].c = 0; flag = num[i].b;
for(int j=0; j<index && flag>1; )
{
if(flag%a[j]==0)
{
flag /= a[j];
num[i].c ++;
}
else
{
j++;
}
}
//原数<=10^12,除遍10^6内的素数后,若该数仍大于0,则一定有且仅一个10^6+1~10^12的质因子
if(flag>1) num[i].c++;
}
sort(num+1, num+n+1, cmp);
sum = 0, ans_min = 1000000000;
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
{
leftt[i] = num[i].b;
sum += num[i].c;
if(num[i].c!=1) sum++;
}
//cout << "sum: " << sum << endl;
dfs(1);
cout << ans_min << endl;
//for(int i=1; i<=n; i++)
// cout << num[i].b << ": " << num[i].c << endl;
//cout << index << endl;
//cout << a[index-1] << endl;
return 0;
}