dfs即可。0^2 + 10^2 +30^2 = 1000
#include
using namespace std;
int main() {
for(int i=0; i<=40; ++i)
for(int j=i; j<=40; ++j)
for(int k=j; k<=40; ++k)
if(i*i+j*j+k*k==1000) {
cout<<i<<" "<<j<<" "<<k<<endl;
return 0;
}
return 0;
}
0*0 + 10*10 + 30*30 = 1000,所以我的答案是0
这个题可以直接数出来。到2014年11月30日,过了30 - 9 = 21(天)
,到2014年12月31日,过了21 + 31 = 52(天)
,那么到2016年12月31日,过了52 + 365 + 366 = 783(天)
,还有1000 - 783 = 217(天)
。大概217天是7个月,2017年前7个月的天数之和为:31 + 28 + 31 + 30 + 31 + 30 + 31 = 212(天)
, 那么还有217 - 212 = 5(天)
,答案就是2017-08-05 。
当然我们也可以写程序。写一个函数,给定当前日期和增加的天数,返回若干天以后的日期。代码如下。
#include
using namespace std;
int md[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
bool Leap(int y) {
if(y%400==0||y%4==0&&y%100) return 1;
return 0;
}
int getMonthDay(int y,int m) {
if(m!=2) return md[m];
return md[m]+Leap(y);
}
void add(int &y,int &m,int &d,int add) {
int monthday = getMonthDay(y,m),yday;
if(d+add<=monthday) {
d += add; return;
}
add -= monthday-d;
d = monthday;
for(int i=m+1; i<=12; ++i) {
monthday = getMonthDay(y,i);
if(add>=monthday) m = i, d = monthday, add -= monthday;
else break;
}
if(add==0) return;
if(m<12) {
++m; d = add; return;
}
for(int i=y+1;; ++i) {
yday = 365+Leap(i);
if(add>=yday) add -= yday,++y;
else break;
}
if(add==0) return;
++y;
if(add<=31) {
m = 1; d = add; return;
}
for(int i=1; i<=12; ++i) {
monthday = getMonthDay(y,i);
if(add>=monthday) m = i, d = monthday, add-=monthday;
else break;
}
if(add==0) return;
if(m==12) m = 0;
++m; d = add;
return;
}
int main() {
int y = 2014, m = 11, d = 9, i=1000;
add(y,m,d,i);
printf("%04d-%02d-%02d\n",y,m,d);
return 0;
}
2017-08-05
因为立方在十位数之内,所以这个数并不大,暴力找就好了。
#include
using namespace std;
bool f(int x) {
int cnt[10] = {0};
long long y = x*x;
long long z = y*x;
map<int,int> mp;
while(y) cnt[y%10]++,y/=10;
while(z) cnt[z%10]++,z/=10;
bool ok = true;
for(int i=0; i<10; ++i) {
if(cnt[i]!=1) return false;
}
cout<<x<<endl;
return true;
}
int main(void) {
for(int i=1;; ++i)
if(f(i)) break;
return 0;
}
69
#include
#include
void StringInGrid(int width, int height, const char* s)
{
int i,k;
char buf[1000];
strcpy(buf, s);
if(strlen(s)>width-2) buf[width-2]=0;
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
for(k=1; k<(height-1)/2;k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("|");
printf("%*s%s%*s",_____________________________________________); //填空
printf("|\n");
for(k=(height-1)/2+1; k<height-1; k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
}
int main()
{
StringInGrid(20,6,"abcd1234");
return 0;
}
题目要求就是居中显示一行字符串。%*s这个占位符需要两个参数,第一个参数是(unsigned) int类型的x,代表输出字符串应该站x个字符位置,第二个参数是字符串的头指针。
我们知道,宽度为width,前后要有竖线,那么剩下width-2个位置,字符串的长度为strlen(s),所以左边和右边空格数为:(width-2-strlen(s))/2, 化简后为:(width - strlen(s)) / 2 - 1
(width-strlen(s))/2-1," ",buf,(width-strlen(s))/2-1," "
#include
void test(int x[])
{
int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
if(a*3==b) printf("%d / %d\n", a, b);
}
void f(int x[], int k)
{
int i,t;
if(k>=9){
test(x);
return;
}
for(i=k; i<9; i++){
{t=x[k]; x[k]=x[i]; x[i]=t;}
f(x,k+1);
_____________________________________________ // 填空处
}
}
int main()
{
int x[] = {1,2,3,4,5,6,7,8,9};
f(x,0);
return 0;
}
这是一个很好的通过交换生成全排列的做法,这样解决了递归可能的爆栈问题。
从代码可以看出,每次将第x个数和后面的分别角换,生成新的排列。
{t=x[k]; x[k]=x[i]; x[i]=t;}
52张没有大小王的牌中选13张,问有多少种不同的结果。每个点数的牌有4张。我们也不用容斥了,直接爆搜就好了。每个点数可以取0-4张
,最后每个点数选的牌个数之和为13。
#include
using namespace std;
long long ans = 0;
int r[13];
void dfs(int x,int sum) {
if(x==13) {
if(sum==13) ++ans;
return;
}
for(int i=0;i<=4;++i) {
if(i+sum<=13) dfs(x+1,sum+i);
}
}
int main(void) {
dfs(0,0);
cout<<ans<<endl;
return 0;
}
3598180
一看就是一个Polya定理。数字这么小,直接搜吧。dfs一个长度为12,由RWY
中的字符组成的字符串,判断之前是否有等价的状态。我们知道,手链转动相当于字符串循环移位,手链翻转相当于字符串翻转。所以,没得到一个字符串,将它循环移位12次,加上翻转,24个状态都判断一遍,如果以前没有出现过这个状态,就计数。可以用map,也可以字符串哈希更快。
#include
using namespace std;
char r[15] = "ABCDEFGHIJKL";
int ans = 0;
map<string,int> mp;
string toLeft(int d) {
string s;
for(int i=d;i<=d+11;++i)
{
s += r[i%12];
}
//cout<
return s;
}
void cal()
{
r[12] = '\0';
string str;
bool ok = true;
for(int i=1;i<=12;++i)
{
str = toLeft(i);
if(mp.count(str)) {
ok = false;break;
}
reverse(str.begin(),str.end());
if(mp.count(str)) {
ok = false;break;
}
}
if(ok) {
mp[r] = 1;++ans;
}
}
void dfs(int x,int cr,int cw,int cy)
{
if(x==12){
cal();
return;
}
if(cr>0) {
r[x] = 'R';
dfs(x+1,cr-1,cw,cy);
} if(cw>0) {
r[x] = 'W';
dfs(x+1,cr,cw-1,cy);
} if(cy>0) {
r[x] = 'Y';
dfs(x+1,cr,cw,cy-1);
}
}
int main()
{
dfs(0,3,4,5);
cout<<ans<<endl;
return 0;
}
1170
这个不能赊账,也不能借,和我们做的小学奥数题不同,但贴近实际规则。直接除以3,计数,直到小于3。
#include
using namespace std;
int main() {
int n;
while(cin>>n) {
int ans = n;
int add = n;
while(add>=3) {
ans += add/3;
add = add/3+add%3;
}
cout<<ans<<endl;
}
return 0;
}
每个骰子向上有6中选择,确定了向上的数字后,可以水平旋转,有4中选择。我们设dp[x][y]为垒x个骰子,且第x个骰子朝上的数字为y的个数。那么,我们就可以由dp[x-1][t]来向dp[x][y]递推。只要满足y对面的数字y‘和t不排斥即可。所以,递推的伪代码可以写出来:
for y in range(1,6+1):
dp[1][y] = 1 #第一个骰子朝上的数字为y的情况都为1种
for x in range(2,n+1): #防止第x个骰子
for y in range(1,6+1):
for t in range(1,6+1):
dp[x][y] += can[op(y)][t] * dp[x-1][t]
sum = 0
for y in range(1,6+1):
sum += dp[n][y]
sum = sum * pow(4,n)
我们可以用系数矩阵来描述这个关系:
| dp[x][1] | |1 1 1 1 1 1| |dp[x-1][1]|
| dp[x][2] | |1 1 1 1 1 1| |dp[x-1][2]|
| dp[x][3] | = |1 1 1 1 1 1| * |dp[x-1][3]|
| dp[x][4] | |1 0 1 1 1 1| |dp[x-1][4]|
| dp[x][5] | |0 1 1 1 1 1| |dp[x-1][5]|
| dp[x][6] | |1 1 1 1 1 1| |dp[x-1][6]|
于是乎,我们求出了系数矩阵,就可以矩阵快速幂加速dp了。
对于一对排斥的数字x,y,不能贴着。就是说,如果下层的x朝上,那么上层的op(y)就不饿能朝上,所以can[op(y)][x] = 0, 同理,can[op(x)][y] = 0
我们知道,dp[1][1],dp[1][2], … dp[1][6]均为1,那么用系数矩阵(66)的n-1次方左乘这个dp[1]列向量(61) 的到的新的列向量(6*1)即为dp[n],我们将dp[n]的六个值相加,然后乘以4^n即可。注意答案对1E9+7
取模。
#include
#define LL long long
using namespace std;
const int mod = 1e9+7;
LL a[6][6];
LL fast_pow(LL a,LL b)
{
if(a==0) return 0;
LL ans = 1;
while(b) {
if(b&1) ans = ans*a%mod;
a = a*a%mod;
b>>=1;
}
return ans;
}
void Mul(LL a[6][6],LL b[6][6]) {
LL c[6][6] = {0};
for(int i=0;i<6;++i)
for(int j=0;j<6;++j)
for(int k=0;k<6;++k)
c[i][j] = (c[i][j]+a[i][k]*b[k][j]%mod)%mod;
for(int i=0;i<6;++i)
for(int j=0;j<6;++j)
a[i][j] = c[i][j];
}
void Mat_fast_pow(LL a[6][6],int n) {
LL p = fast_pow(4ll,n); --n;
LL ans[6][6] = {0};
for(int i=0;i<6;++i) ans[i][i] = 1;
while(n) {
if(n&1) Mul(ans,a);
Mul(a,a);
n>>=1;
}
LL res = 0;
for(int i=0;i<6;++i)
for(int j=0;j<6;++j)
res = (res+ans[i][j])%mod;
res = res * p % mod;
cout<<res<<endl;
}
int op(int x) {
return (x+3)%6;
}
int main()
{
int n,m,x,y;
while(cin>>n>>m)
{
for(int i=0;i<6;++i)
for(int j=0;j<6;++j)
a[i][j] = 1;
while(m--){
cin>>x>>y;
--x,--y;
a[op(x)][y] = a[op(y)][x] = 0;
}
Mat_fast_pow(a,n);
}
return 0;
}
7 10 4
1 3 10
2 6 9
4 1 5
3 7 4
3 6 9
1 5 8
2 7 4
3 2 10
1 7 6
7 6 9
1 7 1 0
1 7 3 1
2 5 1 0
3 7 2 1
9
6
8
8
就是说,一个图,所有的边都被破坏,现在要让其中的某些节点相互连通,让我们连一些边,要求最长的边最小化。求最长的边的长度。
我们可以贪心地想,将边按长度(维修时间)从小大排序。然后从小到大连边,知道选出的所有点都互相连通就停止,那么最后连的那一条边即为答案。判断图的连通性可以用并查集。这也就是最小生成树的Kruskal算法的思想。
由于并查集check每次初始化都要O(n), 我们不如二分答案。二分加几条边可以满足连通。
注意有重边和自环,我们可以用map
处理输入的边
#include
#define MP make_pair
#define pb push_back
using namespace std;
const int maxn = 5E4+10;
const int maxm = 2E5+10;
struct Node{
int to,Next,d;
}node[maxm*2];
int head[maxn],tot;
void init(int n) {
memset(head,-1,sizeof(head));
tot = 0;
}
void addedge(int u,int v,int d) {
node[tot].to = v;
node[tot].d = d;
node[tot].Next = head[u];
head[u] = tot++;
}
struct E{
int u,v,d;
E(int _u=0,int _v=0,int _d=0):u(_u),v(_v),d(_d){}
bool operator < (const E& rhs)const {
return d < rhs.d;
}
}edge[maxm];
int pre[maxn];
int Find(int x) {
return x==pre[x]?x:pre[x]=Find(pre[x]);
}
void join(int x,int y) {
int fx = Find(x), fy = Find(y);
if(fx==fy) return;
pre[fy] = fx;
}
int main()
{
int m,n,q,x,y,p,L,R,k,c;
scanf("%d%d%d",&n,&m,&q);
init(n);
map<pair<int,int>,int> mpe;
map<pair<int,int>,int>::iterator it;
pair<int,int> pr;
for(int i=0;i<m;++i) {
scanf("%d%d%d",&x,&y,&p);
if(x==y) continue;
if(x>y) swap(x,y);
pr = make_pair(x,y);
if(!mpe.count(pr)||mpe[pr]>p) mpe[pr] = p;
}
m = 0;
for(it=mpe.begin();it!=mpe.end();++it){
pr = it->first; x = pr.first; y = pr.second;
p = it->second;
addedge(x,y,p);
addedge(y,x,p);
edge[++m] = E(x,y,p);
}
sort(edge+1,edge+m+1);
for(int ca=1;ca<=q;++ca) {
scanf("%d%d%d%d",&L,&R,&k,&c);
int fir = L;
for(;fir<=R;++fir)
if(fir%k==c) break;
map<int,int> mp;
vector<int> v;
int sz = 0,fa;
while(fir<=R)
mp[fir] = 1,v.pb(fir),fir+=k, ++sz;
int left = 0, right = m, mid;
while(right-left>1) {
mid = (left+right)>>1;
for(int i=1;i<=n;++i) pre[i] = i;
for(int i=1;i<=mid;++i) {
join(edge[i].u,edge[i].v);
}
fa = Find(v[0]);
bool ok = true;
for(int i=1;i<sz;++i) {
if(Find(v[i])!=fa) {
ok = false; break;
}
}
if(ok) right = mid;
else left = mid;
}
printf("%d\n",edge[right].d);
}
return 0;
}
2020.3.12
23:17
xzc