Timus Online Judge 1658 Sum of Digits
2015-10-31 11:18
218 查看
这道题做了快24个小时(昨天十一点做到今天十一点),一直不知道为什么会WA在第四组,而且WA了12次,突然AC,感觉幸福来得太突然。
题目大意:是否存在数N,使得N上的每一位相加得s1,每一位的平方相加得s2?如果存在输出最小的数N,不存在则输出"No solution",如果最小的数N大于100位也要输出“No solution”。其中,s1和s2的范围在0~10000之间。
首先来复习一下一个数学知识:有没有不相等的四个正整数,使得a^2 + b^2 = c^2 + d^2 和 a + b = c + d同时成立。两个式子变下型,(a + c) * (a - c) = (d + b) * (d - b), a - c = d - b。那么得出a + c = d + b, 再联立a - c = d - b, 可以得出 a = d, b = c。
所以,根本找不到这样的四个数,那么我们就可以放心了。基于这一点,我们就可以看出,在N的位数确定为K的情况下,s1 和 s2所对应的N中所包含的k个数就都已经确定了,只需要按照升序排好,它就是这么多位中最小的数。
其实dp方程真的不会太难,我的dp[i][j]是表示的各位的和为i且各位的平方的和为j的数中,最小的数的位数,那么dp[i + k][j + k * k] = min{dp[i][j] + 1}。然后记录一下dp[i][j]到dp[i + k][j + k * k]是选的数字几,也就是保存一下k,保存一下路径就OK了。
其实,分析这道题目和其它的许多题目类似, 都是从时间复杂度和空间复杂度入手,先想想怎么才能不会TLE、MLE,这样才能找到正确的解题思路,千万不能纠结。
代码甚是丑陋,请多指教!
#include <iostream>
#include <algorithm>
#define pii pair<int, int>
#define x first
#define y second
using namespace std;
int dp[901][8101], path[901][8101];
void Init() {
for (int i = 0; i <= 900; ++i) {
for (int j = 0; j <= 8100; ++j) {
dp[i][j] = 101;
}
}
dp[0][0] = 0;//初始化,由dp[0][0]推到dp[1][1]、dp[2][4]、dp[3][9]、dp[4][16]……
for (int i = 0; i <= 900; ++i) {
for (int j = i; j <= 8100; ++j) {
for (int k = 1; k <= 9; ++k) {//或许会枚举出一些重复的情况,但是如果从path[i][j]的值开始枚举的话也许会漏掉一些解,之前就在这里一直WA
if (i + k > 900 || j + k * k > 8100) continue;
if (dp[i + k][j + k * k] > dp[i][j]) {
dp[i + k][j + k * k] = dp[i][j] + 1;
path[i + k][j + k * k] = k;//更新的时候记得保存路径
}
}
}
}
}
int main() {
Init();
int T;
cin >> T;
for (int I = 1; I <= T; ++I) {
int s1, s2;
cin >> s1 >> s2;
if (s1 > 900 || s2 > 8100 || s1 > s2 || dp[s1][s2] > 100) cout << "No solution" << endl;
else {
string ans;
pii p(s1, s2);
while (p.x && p.y) {//路径还原
int t = path[p.x][p.y];
p.x -= t, p.y -= t * t;
ans += t + '0';
}
sort(ans.begin(), ans.end());
cout << ans << endl;
}
}
return 0;
}
题目大意:是否存在数N,使得N上的每一位相加得s1,每一位的平方相加得s2?如果存在输出最小的数N,不存在则输出"No solution",如果最小的数N大于100位也要输出“No solution”。其中,s1和s2的范围在0~10000之间。
首先来复习一下一个数学知识:有没有不相等的四个正整数,使得a^2 + b^2 = c^2 + d^2 和 a + b = c + d同时成立。两个式子变下型,(a + c) * (a - c) = (d + b) * (d - b), a - c = d - b。那么得出a + c = d + b, 再联立a - c = d - b, 可以得出 a = d, b = c。
所以,根本找不到这样的四个数,那么我们就可以放心了。基于这一点,我们就可以看出,在N的位数确定为K的情况下,s1 和 s2所对应的N中所包含的k个数就都已经确定了,只需要按照升序排好,它就是这么多位中最小的数。
其实dp方程真的不会太难,我的dp[i][j]是表示的各位的和为i且各位的平方的和为j的数中,最小的数的位数,那么dp[i + k][j + k * k] = min{dp[i][j] + 1}。然后记录一下dp[i][j]到dp[i + k][j + k * k]是选的数字几,也就是保存一下k,保存一下路径就OK了。
其实,分析这道题目和其它的许多题目类似, 都是从时间复杂度和空间复杂度入手,先想想怎么才能不会TLE、MLE,这样才能找到正确的解题思路,千万不能纠结。
代码甚是丑陋,请多指教!
#include <iostream>
#include <algorithm>
#define pii pair<int, int>
#define x first
#define y second
using namespace std;
int dp[901][8101], path[901][8101];
void Init() {
for (int i = 0; i <= 900; ++i) {
for (int j = 0; j <= 8100; ++j) {
dp[i][j] = 101;
}
}
dp[0][0] = 0;//初始化,由dp[0][0]推到dp[1][1]、dp[2][4]、dp[3][9]、dp[4][16]……
for (int i = 0; i <= 900; ++i) {
for (int j = i; j <= 8100; ++j) {
for (int k = 1; k <= 9; ++k) {//或许会枚举出一些重复的情况,但是如果从path[i][j]的值开始枚举的话也许会漏掉一些解,之前就在这里一直WA
if (i + k > 900 || j + k * k > 8100) continue;
if (dp[i + k][j + k * k] > dp[i][j]) {
dp[i + k][j + k * k] = dp[i][j] + 1;
path[i + k][j + k * k] = k;//更新的时候记得保存路径
}
}
}
}
}
int main() {
Init();
int T;
cin >> T;
for (int I = 1; I <= T; ++I) {
int s1, s2;
cin >> s1 >> s2;
if (s1 > 900 || s2 > 8100 || s1 > s2 || dp[s1][s2] > 100) cout << "No solution" << endl;
else {
string ans;
pii p(s1, s2);
while (p.x && p.y) {//路径还原
int t = path[p.x][p.y];
p.x -= t, p.y -= t * t;
ans += t + '0';
}
sort(ans.begin(), ans.end());
cout << ans << endl;
}
}
return 0;
}
相关文章推荐
- poj1258prim算法
- mil,mm与inch之间的转换
- poj1258prim算法
- sublime text 全局搜索
- 高并发与锁(二)
- 项目视频界面横屏返回竖屏,oncreate执行2次
- linux服务开机启动
- 免秘钥SSH登陆,切记切换用户
- [TwistedFate]实例变量可⻅度、⽅法
- 浙大PAT(PAT Basic Level) 1045—— 快速排序
- iOS线程报错
- winfrom 中boderstyle 设置为none后不能移动的问题
- 安装mysql
- Spring Security的核心拦截器
- slf4j+log4j
- 数据库-除
- 数据挖掘——总结 【未完待续】
- innocence
- git访问https
- LDAP