2012 Multi-University Training Contest 5
官方题解:2012 Multi-University Training Contest 5 Solution
1003 Gold miner 分组背包,详见《背包问题九讲》。 用点积判断是否在一条直线上。
#include
#include
#include
#include
using namespace std;
const int MAXN = 210;
struct gold
{
int x, y, t, v;
}g[MAXN];
int dp[40010];
bool sameGroup(int a, int b)
{
return g[a].x * g[b].y == g[b].x * g[a].y;
}
bool comp(const gold &a;, const gold &b;)
{
return a.y < b.y;
}
int main()
{
int n, t;
int group[MAXN][MAXN];
int caseno = 0;
while (scanf("%d%d", &n;, &t;)!= EOF)
{
int groupCount = 0;
memset(group, 0, sizeof(group));
for (int i = 0; i < n; i++)
scanf("%d%d%d%d", &g;[i].x, &g;[i].y, &g;[i].t, &g;[i].v);
sort(g, g + n, comp);
for (int i = 0; i < n; i++)
{
bool found = false;
for (int j = 1; j <= groupCount; j++)
{
if (sameGroup(i, group[j][1]))
{
gold* prev = &g;[group[j][group[j][0]]];
++group[j][0];
group[j][group[j][0]] = i;
g[i].t += prev->t;
g[i].v += prev->v;
found = true;
break;
}
}
if (found) continue;
++groupCount;
++group[groupCount][0];
group[groupCount][1] = i;
}
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= groupCount; i++)
{
for (int time = t; time >= 0; time--)
{
for (int j = 1; j <= group[i][0]; j++)
{
//if (time - g[group[i][j]].t < 0) break;
if (time - g[group[i][j]].t >= 0)
dp[time] = max(dp[time], dp[time - g[group[i][j]].t]
+ g[group[i][j]].v);
}
}
}
printf("Case %d: %d\n", ++caseno, dp[t]);
}
return 0;
}
1004 History repeat itself 不停地求sqrt(N),得到比N小的新平方数的个数,然后加上这个数,直到找不到新的平方数为止。此时正好有N个非平方数(新加的数都是平方数)。然后可以很方便地推出公式求和。
#include
#include
#include
using namespace std;
int main()
{
int t, n;
cin >> t;
while (t--)
{
scanf("%d", &n;);
long long m = n;
long long add = sqrt(m);
long long count = 0;
while (add)
{
m += add;
count += add;
add = sqrt(m) - count;
}
cout << m << " ";
long long x = count - 1;
cout << x * (x+1) * (2*x+1)/3+(x+x*x)/2 + (m - count*count + 1) * count << endl;
}
return 0;
}
1007 Permutation 很容易想到这道题其实就是求,一些数相加和为N,它们的最小公倍数有多少种情况。遗憾的是没往下想,直接想DP没思路。 官方题解:由于1不影响最小公倍数,问题转化为相加小于等于N的若干正整数的最小公倍数的可能数。 再考虑,如果两个数不互质,最小公倍数与少一些因子变得互质的情况相同。所以只要考虑这些正整数是质数即可,注意每个数可能出现多次。 dp[i][j]表示前i种质数,构成和为j有多少种情况。 dp[i][j] = dp[i-1][j] + dp[i-1][j-k*prime[i]]
#include
#include
#include
#include
using namespace std;
long long dp[1010][1010];
int main()
{
int n, primeCount = 0;
int p[1000];
for (int i = 2; i < 1010; i++)
{
int s = sqrt(i);
bool prime = true;
for (int j = 2; j <= s; j++)
{
if (i % j == 0)
{
prime = false;
break;
}
}
if (prime) p[++primeCount] = i;
}
int temp;
while (scanf("%d", &n;) != EOF)
{
memset(dp, 0, sizeof(dp));
for (int i = 0; i <= primeCount; i++) dp[i][0] = 1;
long long ans = 0;
for (int i = 1; i <= primeCount; i++)
{
if (n - p[i] < 0)
{
for (int j = 0; j <= n; j++) ans += dp[i-1][j];
break;
}
temp = p[i];
for (int j = n; j > 0; j--)
{
dp[i][j] = dp[i - 1][j];
}
while (n - temp >= 0)
{
for (int j = n; j > 0; j--)
{
if (j - temp < 0) break;
dp[i][j] += dp[i - 1][j - temp];
}
temp *= p[i];
}
}
cout << ans << endl;
}
return 0;
}
1011 Xiao Ming’s Hope 找规律,这题我用cin居然超时,不知道为什么。
#include
#include
using namespace std;
int main()
{
int n;
while (scanf("%d", &n;) != EOF)
{
int count = 0;
do
{
if (1 & n) ++count;
n >>= 1;
} while(n);
printf("%d\n", 1 << count);
}
return 0;
}
comments powered by Disqus