第一周总结周报(1.22-1.27)

目录

一.1.22(洛谷)

        1.P8761

        2.P8195

        3.P8651

        4.P9240

        5.P8627

        6.P8647**

        7.P8637**

        8.P8625**(树状dp)

        9.P8638**(最长公共子序列(LCS))

        10.P8774**

二.1.24(洛谷)

        1.P8506

        2.P8507

        3.P8508**

        4.B3691**

        5.P1048

        6.P7060

        7.P8889**

        8.P6225**

        9.U360489**

        10.P1352**

三.1.26(洛谷)

        1.P6056

        2.P1765

        3.P4305

        4.P1145

        5.P8742**

        6.P6473**

        7.P8808

        8.[ABC325E]**

        9.P8786**

        10.P1685**

四.反思总结


一.1.22(洛谷)

        1.P8761

该题就是一个大小写的转换。

#include<stdio.h>
#include<string.h>
int main(void)
{
	int k;
	char a[101];
	scanf("%s", a);
	k=strlen(a);
	for(int i=0;i<k;i++)
	{
		if(a[i]>='a'&&a[i]<='z')
		{
			a[i]-=32;
		}
	}
	puts(a);
	return 0;
}

        2.P8195

该题就是检测一个连续的字符串

#include<stdio.h>
#include<string.h>
int main(void)
{
	int k, c=0;
	char a[400001];
	scanf("%s", a);
	k=strlen(a);
	for(int i=0;i<=k-8;i++)
	{
		if(a[i]=='c'&&a[i+1]=='h'&&a[i+2]=='u'&&a[i+3]=='a'&&a[i+4]=='n'&&a[i+5]=='z'&&a[i+6]=='h'&&a[i+7]=='i')
			c++;
	}
	printf("%d", c);
	return 0;
}

        3.P8651

一开始我的想法是去考虑它年月日、月日年、日月年三种排列分别的可能情况。但后面发现在输出时,输出的结果有不满足条件的。

后面改变想法:去考虑19600101到20591231中满足条件的情况。

#include<stdio.h>
int nian(int y);
int yue(int y,int m);
int ri[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
int main(void)
{
	int a, b, c, y, m, d, k;
	scanf("%d/%d/%d", &a, &b, &c);
	for(int i=19600101;i<=20591231;i++)
	{
		y=i/10000;
		m=i/100%100;
		d=i%100;
		k=0;
		if(m<=0||m>=13)
			continue;
		if(d<=0||d>yue(y,m))
			continue;
		if(y%100==a&&m==b&&d==c)
			k=1;
		if(m==a&&d==b&&y%100==c)
			k=1;
		if(d==a&&m==b&&y%100==c)
			k=1;
		if(k==1)
			printf("%02d-%02d-%02d\n",y,m,d);
	}
	return 0;
}
int nian(int y)
{
	if(y%400==0||(y%4==0&&y%100!=0))
		return 1;
	return 0;
}
int yue(int y,int m)
{
	if(m==02&&nian(y)==1)
		return 29;
	else
		return ri[m];
}

        4.P9240

该题我的一开始的想法就是考虑o%x==0和o%x!=0两种情况,然后在去判断o/x的值做最大值,取所有最大值的最小值,还有判断不小于o/(x+1)的值做最小值,取所以最小值的最大值。以此来判断出所以数组的共同最大值和最小值。

#include<stdio.h>
#include<math.h>
#include<algorithm>
int main(void)
{
	int n, o, x, max=1000000000, min=-1000000000, t;
	scanf("%d", &n);
	for(int i=0;i<n;i++)
	{
		scanf("%d%d", &o, &x);
		if(o%x==0)
		{
			if(o/x<max)
				max=o/x;
			t=ceil((o*1.0)/(x+1));
			if(t>min)
				min=t;
		}
		if(o%x!=0)
		{
			t=o/x;
			if(t<max)
				max=t;
			t=ceil((o*1.0)/(x+1));
			if(t>min)
				min=t;
		}
	}
	printf("%d %d", min, max);
	return 0;
}

        5.P8627

该题我使用了函数,通过反复调用函数,去计算实际能得到的饮料数。函数的功能是去计算当前的n(饮料瓶)能去换多少个新n(饮料瓶)。

#include<stdio.h>
int a(int n);
int s=0;
int main(void)
{
	int n;
	scanf("%d", &n);
	s+=n;
	for(;n!=0;)
	{
		s+=n/3;
		n=a(n);
	}
	printf("%d", s);
	return 0;
}
int a(int n)
{
	int k;
	if(n%3==0)
	{
		n=n/3;
	}
	else
	{
		k=n%3;
		n=n/3+k;
	}
	if(n<3)
		return 0;
	else
		return n;
}

        6.P8647**

该题刚开始我没做出来。

后面网上看了一下其他人的做法,先计算每一个边长下可以切分的巧克力数量,再与孩子数量做比较即可,那么每次选择的边长可以用二分查找,而每一块巧克力可以切分的目标大小巧克力数量就是(n/r)* (m/r) n、m分别为当前巧克力的长度,宽度。

#include<iostream>
using namespace std;

typedef long long LL;
const int N = 100010;
int a[N],b[N];
int n,k;

bool check(int mid){
	LL ans=0;
	for(int i=0;i<n;i++)ans+=(a[i]/mid)*(b[i]/mid);
	if(ans>=k)return true;
	else return false;
}

int main(){
	cin>>n>>k;
	
	int ans=0;
	for(int i=0;i<n;i++){
		scanf("%d%d",&a[i],&b[i]);
		ans=max(a[i],max(ans,b[i]));
	}
	int l=0,r=2*ans;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid))l=mid+1;
		else r=mid; 
	}
	printf("%d",l-1);
	
	return 0;
}

        7.P8637**

这道题我一开始也是没做出来。

后面的想法是这样的:先判断该元素是否在属于它的位置上,如果不在,就找到它然后交换,记录交换的次数。

#include<stdio.h>
int main()
{
    int a[10010],t,s=0,n,i,j,k;
    scanf("%d",&n);
    for(i=1; i<=n; i++)
        scanf("%d",&a[i]);
    for(i=1; i<=n; i++)
        if(a[i]!=i)
        {
            for(j=1; j<=n; j++)
                if(a[j]==i)
                {
                    t=a[i];
                    a[i]=i;
                    a[j]=t;
                    break;
                }
            s++;
        }
    printf("%d\n",s);
    return 0;
}

        8.P8625**(树状dp)

该题考察的知识点是树状dp,不过我当时还不会。。。

#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 100010;
int n;
int w[N];
ll f[N];
ll res;
vector<int> g[N];
void dfs(int u, int fa){
	for (int i = 0; i < g[u].size(); i ++ ){
		int son = g[u][i];
		if (son != fa){
			dfs(son, u);
			f[u] += max(0ll, f[son]);
		}
	}
	res = max(res, f[u]);
}
int main(){
	cin >> n;
	for (int i = 1; i <= n; i ++ ) cin >> w[i], f[i] = w[i];
	for (int i = 0; i < n - 1; i ++ ){
		int u, v;
		scanf("%d%d", &u, &v);
		g[u].push_back(v);
		g[v].push_back(u); 
	}
	dfs(1, -1);
	cout << res << endl; 
	return 0;
}

        9.P8638**(最长公共子序列(LCS))

这道题是一个求最长公共子序列(LCS)的题目。而求LCS我们可以使用动态规划(dp)的办法。

状态设置:

f[i][j]表示的是序列Si = {x1,x2,x3,...,xi}与序列Sj = {y1,y2,y3,...,yj}的最长公共子序列的长度。

状态转移方程:

        当i=0,j=0时,f [ i ][ j ] = 0;

        当xi = yi时,f [ i ][ j ] = 1 + f [ i - 1 ][ j - 1 ];

        当xi != yi时,f [ i ][ j ] = max( f [ i - 1 ][ j ] , f [ i ][ j - 1 ] );

最长子序列即为f [ n ][ m ](n为s1的长度,m为s2的长度)。

#include <iostream>
using namespace std;
int n;
int f[1001][1001];
int LCS(string s1,string s2)
{
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= n;j++)
		{
			if(s1[i-1] == s2[j-1])
			{
				f[i][j] = 1 + f[i-1][j-1];
			}
			else
			{
				f[i][j] = max(f[i-1][j],f[i][j-1]);
			}
		}
	}
	return f[n][n];
}
int main()
{
	string s1;
	while(cin >> s1)
	{
		n = s1.length();
		string s2 = s1;
		for(int i = 0;i < n;i++)
		{
			s2[n-i-1] = s1[i];
		} 
		int ans = n-LCS(s1,s2);
		cout << ans << endl;
	} 
	
	return 0;
}

        10.P8774**

额,这道题,额,弄了很久,也没弄明白,看了网上很多解法,才搞明白了一点,先附的代码,继续研究吧。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll qpow(ll num, ll pow, ll p){
	ll ans=1;
	while(pow){
		if(pow&1) ans=ans*num;
		num*=num;
		ans%=p;
		num%=p;
		pow >>= 1;
	}
	return ans;
}
int main(){
	ll x,y,n;
	ll p=998244353;
	cin>>n;
	ll E[n];
	E[0]=0;
	ll i;
	for(i=1;i<=n;i++){
		cin>>x;
		cin>>y;
		E[i]=((E[i-1]+1)%p*(y%p))%p;
		E[i]*=qpow(y-x,p-2,p);	
		E[i]%=p;
	}
	cout<<E[n];
	return 0;
}

二.1.24(洛谷)

        1.P8506

该题我的想法是从开头开始检测,先排除掉开头不为'#'与' '的情况,当检测到'#'时,判断其后一位是否为' ',是的话继续判断其后续有无非' '的字符。

注意:该段代码需要在C语言、C++98、C++11的编译环境下才能编译成功。

#include<stdio.h>
#include<string.h>
int main(void)
{
	int n, c=0, m, k;
	char a[11][101];
	scanf("%d", &n);
	getchar();
	for(int i=0;i<n;i++)
	{
		gets(a[i]);
		m=strlen(a[i]);
		for(int j=0;j<m-1;j++)
		{
			k=0;
			if(a[i][j]!='#'&&a[i][j]!=' ')
			{
				break;
			}
			if(a[i][j]=='#')
			{
				if(a[i][j+1]==' ')
				{
					for(int o=j+1;o<m;o++)
					{
						if(a[i][o]!=' ')
						{
							c++;
							k=1;
							break;
						}
					}
				}
				else
				{
					break;
				}
			}
			if(k==1)
			{
				break;
			}
		}
	}
	printf("%d", c);
	return 0;
}

        2.P8507

该题的思路就是每人有1门科目没通过,则每人科目不通过的概率为1/a,所以b人每门科目的不通过的人为(floor)(b/a)。不通过率即为(floor)(b/a)/b。

#include<stdio.h>
#include<math.h>
int main(void)
{
	double a, b;
	scanf("%lf%lf", &a, &b);
	printf("%.16lf", (floor)(b/a)/b);
	return 0;
}

        3.P8508**

这道题我一开始的思路是枚举天数。对于每一天,判断如果做下一个任务,是否满足题目条件。重复这个过程,可以做的任务则做,否则直接睡大觉。

(但超时了。。。)

优化了一下:倒序插入任务,再算出前面需要睡几天大觉。

#include<bits/stdc++.h>
using namespace std;
long long n,x,p,q,i=1,sum,t,w;
int main(){
	cin>>n>>x>>p>>q;
	while(n--){
		cin>>w;
		if((x-t-w+sum)*q>=i*p*x&&x-t>w){
			t+=w; 
		}
		else{
			sum+=x-t;
			i++;
			long long l=ceil((q*(sum+x-w)-p*i*x)*1.0/(x*p-x*q));
			if(l>0){
				sum+=x*l;
				i+=l;
			}
			t=w;
		}
	}
	cout<<i;
	return 0;
}

        4.B3691**

这道题我的思路是先所有奇数都不是优秀的拆分,因为它含有2^0,而偶数从它所含有的最大2次方进行输出,直至结果为0时返回0。

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	long long cf[32];
	cf[2]=2;cf[1]=1;
	cin>>n;
	if(n%2==1){
		cout<<"-1"<<endl;
		return 0;
	}
	for(int i=3;i<=27;i++)
		cf[i]=2*cf[i-1];
	int n1=n;
	for(int i=27;i>=2;i--){
		if(n==0)
			return 0;
		if(cf[i]<=n1){
			n1=n1-cf[i];
			cout<<cf[i]<<" ";
		}
	}
}

        5.P1048

小背包问题

#include<stdio.h>
#include<algorithm>
int main(void)
{
	int m,t;
	int a[105],b[105];
	int c[105][1005];
	scanf("%d%d", &t, &m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d", &a[i], &b[i]);
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=t;j++)
		{
			c[i][j]=c[i-1][j];
			if(j>=a[i])
			{
				c[i][j]=std::max(c[i-1][j],c[i-1][j-a[i]]+b[i]);
			}
		}
	}
	printf("%d", c[m][t]);
	return 0;
}

        6.P7060

额,额,嘿嘿,因为其实这道题数据并不多,思路也不复杂,所以我直接把每一种情况都列了一种可能出来。

#include<stdio.h>
int main(void)
{
	int n;
	scanf("%d", &n);
	if(n<8||n>26)
	{
		printf("Impossible");
	}
	else
	{
		if(n==8)
			printf("11:11");
		if(n==9)
			printf("11:17");
		if(n==10)
			printf("11:14");
		if(n==11)
			printf("11:13");
		if(n==12)
			printf("01:11");
		if(n==13)
			printf("01:17");
		if(n==14)
			printf("01:14");
		if(n==15)
			printf("01:12");
		if(n==16)
			printf("01:01");
		if(n==17)
			printf("07:01");
		if(n==18)
			printf("04:01");
		if(n==19)
			printf("02:01");
		if(n==20)
			printf("00:01");
		if(n==21)
			printf("00:07");
		if(n==22)
			printf("00:04");
		if(n==23)
			printf("00:02");
		if(n==24)
			printf("00:09");
		if(n==25)
			printf("08:00");
		if(n==26)
			printf("08:08");
	}
	return 0;
}

        7.P8889**

        8.P6225**

这道题我没有什么思路。

然后我在网课看题解时看到了这一种思路:

只有当奇偶性相同的时候才有答案,而答案就是l,l+2,......,r。

但是这里需要注意修改操作

对于异或树状数组的修改 ,我们仍然可以使用上面那个性质。

const int N  = 2e5+10;
int t1[N],t2[N],a[N];
int n,m;

int lowbit(int x){
	return x & (-x);
}

void add(int *C,int x,int k){
	for(int i = x;i <=  n ; i += lowbit(i))
	C[i]^=k;
}

int query(int *C,int x){
	int sum =  0;

	for(int i = x; i ; i-=lowbit(i))
	sum^=C[i];

	return sum;
}
void solve()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];

		if(i&1)
		add(t1,i,a[i]);
		else
		add(t2,i,a[i]);

	}
	for(int i=1;i<=m;i++){
		int op,x,y;
		cin>>op>>x>>y;
		if(op == 1){
			if(x%2 !=0)
			{
				add(t1,x,a[x]^y);
				a[x] = y;
				}
			else{
				add(t2,x,a[x]^y);
				a[x] = y;
			}
		}else{
			if(((x&1)^(y&1)))
			cout<<0<<endl;
			else
			{
				if(x%2 !=0 ){
                    int l = query(t1,y);
                    int r = query(t1,x-1);
                    int ans = l^r;
					cout<<ans<<endl;
				}else{
				    int l = query(t2,y);
                    int r = query(t2,x-1);
                    int ans = l^r;
					cout<<ans<<endl;
				}
			}
		}
	}

}

        9.U360489**

        10.P1352**

三.1.26(洛谷)

        1.P6056

该题好像没什么需要注意的点,吧?

就是有一个循环去一天一天的计算当天恢复的和感染的人数,并为当天结束时的易感人数、感染人数、恢复人数重新赋值。

有一个点就是当天的新增感染人数大于易感人数时,新增感染人数按易感人数算。

#include<stdio.h>
#include<math.h>
int main(void)
{
	int S, I, R=0, n, x, z;
	double b, y;
	scanf("%d%d%d", &S, &I, &n);
	scanf("%lf%lf", &b, &y);
	for(int i=0;i<n;i++)
	{
		x=ceil(b*S*I);
		z=ceil(y*I);
		if(x>S)
		{
			x=S;
		}
		R=R+z;
		S=S-x;
		I=I-z+x;
	}
	printf("%d %d %d", S, I, R);
	return 0;
}

        2.P1765

我的想法就是把每种情况都列出来。

#include<stdio.h>
#include<string.h>
int main(void)
{
	int k, c=0;
	char a[201];
	gets(a);
	k=strlen(a);
	for(int i=0;i<k;i++)
	{
		if(a[i]==' ')
		{
			c+=1;
		}
		if(a[i]=='a'||a[i]=='d'||a[i]=='g'||a[i]=='j'||a[i]=='m'||a[i]=='p'||a[i]=='t'||a[i]=='w')
		{
			c+=1;
		}
		if(a[i]=='b'||a[i]=='e'||a[i]=='h'||a[i]=='k'||a[i]=='n'||a[i]=='q'||a[i]=='u'||a[i]=='x')
		{
			c+=2;
		}
		if(a[i]=='c'||a[i]=='f'||a[i]=='i'||a[i]=='l'||a[i]=='o'||a[i]=='r'||a[i]=='v'||a[i]=='y')
		{
			c+=3;
		}
		if(a[i]=='s'||a[i]=='z')
		{
			c+=4;
		}
	}
	printf("%d", c);
	return 0;
}

        3.P4305

我一开始的想法是先读取一个数组,然后对数组中的每个元素进行判断,如果该元素已经出现过了,则把它赋值为-1,否则不变,最后在输出时进行判断,如果当前元素为-1,则不输出。(结果超时了,呜呜┭┮﹏┭┮)

然后我就投机取巧用了map,,,嘿嘿。

#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
	ios::sync_with_stdio(0); cin.tie(0);
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		unordered_map<int, bool> mp;
		for (int i = 1; i <= n; i++) {
			int a;
			cin >> a;
			if (!mp.count(a)) {
				cout << a << ' ';
				mp[a] = true;
			}
		}
		cout << endl;
	}
	return 0;
}

        4.P1145

看到这道题,一开始我思路是懵的,然后发现这道题就是一个条件判断,即当报数遇到好人时,结束,把报的数+1,重新开始判断,如此循环,直至首个满足条件的值出现。

#include<stdio.h>
int main(void)
{
	int k, t, p;
	scanf("%d", &k);
	t=k+1;
	for(;;)
	{
		p=0;
		for(int i=0;i<k;i++)
		{
			p=(p+t-1)%(2*k-i);
			if(p<k)
				break;
			if(i==k-1)
			{
				printf("%d", t);
				goto end;
			}
		}
		t++;
	}
	end:
	return 0;
}

        5.P8742**

这道题可以去遍历所有可能的重量,若某重量w可以被表示,则分为一下几种情况:

        1.w+m也可以被表示(将w和m都放到砝码盘)

        2.若w > m ,那么 w-m 也可以被表示(将m放到物品栏,w放到砝码盘)

        3.若w  < m,那么m-w 也可以被表示 (将m放到砝码盘,w放到物品盘)

#include<iostream>
#include<queue>
#include<math.h>
using namespace std;
 
const int N = 100005;
int n;
bool f[N];
int ans;
int a;
queue<int>q;
 
int main()
{
    cin>>n;
    f[0]=true;
    for(int i=1;i<=n;++i)
    {
        cin>>a;
        for(int j=0;j<N;++j)
        {
            if(f[j])
            {
                q.push(abs(j-a));
                if(j+a<N) q.push(j+a);
            }
        }
        while(!q.empty())
        {
            f[q.front()]=true;
            q.pop();
        }
    }
 
    for(int i=1;i<N;++i) if(f[i]) ans++;
 
    cout<<ans<<endl;
 
    return 0;
}

        6.P6473**

这道题可以用贪心,每一次取大的,然后用二分判断每一次加在一起够不够。

#include<bits/stdc++.h>
#define maxn 200001
using namespace std;
int n,L,v,m,t;
long long a[maxn],sum[maxn];
bool cmp(const int &x,const int &y){
	return x>y;
}
int main(){
	scanf("%d%d%d",&n,&L,&v);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
	scanf("%d",&m);
	while(m--){
		scanf("%d",&t);
		int l=-1,r=n+1,mid;
		while(l+1<r){
			mid=(l+r)>>1;
			if(sum[mid]+L<=(long long)t*v)l=mid;
			else r=mid;
		}
		if(r>n)printf("-1\n");
		else printf("%d\n",r);
	}
	return 0;
}

        7.P8808

这道题我一开始的想法就是生成一个斐波那契数列的数组,与读取的数组进行一一比较,用c去记录数据不同的个数。

(结果又超时了,呜呜┭┮﹏┭┮)

然后我注意到题目给出的数据中数组的元素值的范围为1到10的6次方。而斐波那契数列的第31个元素的值就大于了10的6次方,故只需要判断前30项就行,如果n大于30,则大于30的部分必定是不同的。

(结果还是不对,o.O)

然后我又认真看了一下题目,原来是斐波那契数组,其中一个条件是a0=a1,而不是a0=a1=1。所以可能出现数据是斐波那契数列的倍数,所以我又用了循环去找最小的c。

#include<stdio.h>
#include<algorithm>
int ahunshu(int n);
int a[30];
int main(void)
{
	int n, c, k=0, minc=100001;
	int b[30];
	scanf("%d", &n);
	if(n>30)
	{
		k=n-30;
		n=30;
	}
	if(n<=30)
	{
		for(int i=1;i<=n;i++)
		{
			a[i-1]=ahunshu(i);
		}
		/*
		  for(int i=0;i<n;i++)
		  {
		  printf("%d", a[i]);
		  }
		  printf("\n");
		 */
		for(int i=0;i<n;i++)
		{
			scanf("%d", &b[i]);
		}
		/*
		  for(int i=0;i<n;i++)
		  {
		  printf("%d", a[i]);
		  }
		  printf("\n");
		  for(int i=0;i<n;i++)
		  {
		  scanf("%d", &b[i]);
		  if(b[i]!=a[i]&&b[i]%a[i]!=0)
		  {
		  c++;
		  }
		  }
		 */
		for(int i=1;i<=1e6;i++)
		{
			c=0;
			for(int j=0;j<n;j++)
			{
				c+=int(b[j]!=i*a[j]);
				/*
				  if(b[j]!=i*a[j])
				  {
				  c++;
				  }
				 */
			}
			minc=std::min(c, minc);
		}
		printf("%d", k+minc);
	}
	return 0;
}
int ahunshu(int n)
{
	if(n==1||n==2)
	{
		return 1;
	}
	else
	{
		return a[n-2]+a[n-3];
	}
}

        8.[ABC325E]**

这道题我基本没什么思路,看了挺久网上的题解,才稍微明白了一点点。

我看到一个思路是:

建一个分层图跑 dijkstra 就可以了。第一个图G1全部以坐车需要的时间为边权,第二个图 G 2 G2G2 全部以坐高铁需要的时间为边权。因为坐车可以转坐高铁,所以 G 1 G1G1 中每个点向 G 2 G2G2 中对应的点连一条边权为 0 00 的边就好了。

#include<bits/stdc++.h>
using namespace std;
int n,A,B,C;
int a[1010][1010];
struct node{
	int v,w;
	bool operator<(const node &b)const{
		return w>b.w;
	}
};
vector<node> G[200010];
int dis[200010];
void Dijkstra(){
	dis[1]=0;
	priority_queue<node> q;
	q.push({1,0});
	while(!q.empty()){
		int u=q.top().v;
		q.pop();
		for(auto x:G[u]){
			int v=x.v,w=x.w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				q.push({v,dis[v]});
			}
		}
	}
}
signed main(){
	memset(dis,0x3f,sizeof(dis));
	cin>>n>>A>>B>>C;
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>a[i][j];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			G[i].push_back({i+n,0});
			G[i].push_back({j,A*a[i][j]});
			G[i+n].push_back({j+n,B*a[i][j]+C});
		}
	}
	Dijkstra();
	cout<<min(dis[n],dis[n+n])<<endl;
	return 0;
}

        9.P8786**

使用三维数组dp[i][j][k]表示在遇到第i个店和第j个花,以及遇到k个花的情况下的方案数。

#include<bits/stdc++.h>
using namespace std;
int mod = 1e9 + 7;
long long dp[105][105][105] = {0};
int main()
{
    int n,m;
    cin >> n >> m;
    dp[0][0][2] = 1;
    dp[0][1][1] = 1;
    dp[0][2][0] = 1;
    for (int i = 0; i <= 7; i++)
    {
        dp[i][0][int(pow(2, i + 1))] = 1;
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            for (int k = 0; k <= m; k++)
            {
                if (k % 2 == 0)
                    dp[i][j][k] = (dp[i - 1][j][k / 2] + dp[i][j - 1][k + 1]) % mod;
                else
                    dp[i][j][k] = dp[i][j - 1][k + 1];  
            }
        }
    }
    cout << dp[n][m-1][1] << endl;
    return 0;
}

        10.P1685**

这道题又要用拓扑排序进行统计(我还搞不太明白。。。)不过它直接是加法原理和乘法原理上去就行了。(稍微简单了一丢丢吧,没搞那么复杂)。

#include <cstdio>
#include <queue>
#define ll long long
using namespace std;
const int N=10010;
const int M=50010;
const ll mod=10000;
int n,m,s,t,in[N];
int head[N],to[M],Next[M],cnt;
ll t0,edge[M],Cnt[N],len[N];
void add(int u,int v,ll w)
{
    to[++cnt]=v;Next[cnt]=head[u];edge[cnt]=w;head[u]=cnt;
}
void init()
{
    scanf("%d%d%d%d%lld",&n,&m,&s,&t,&t0);
    int u,v;ll w;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%lld",&u,&v,&w);
        if(u!=v)
        {
            add(u,v,w);
            in[v]++;
        }
    }
}
queue <int > q;
void work()
{
    q.push(s);
    Cnt[s]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=Next[i])
        {
            int v=to[i];ll w=edge[i];
            in[v]--;
            Cnt[v]=(Cnt[u]+Cnt[v])%mod;
            len[v]=((len[v]+len[u])%mod+w*Cnt[u]%mod)%mod;
            if(!in[v])
                q.push(v);
        }
    }
    ll ans=((Cnt[t]-1)*t0%mod+len[t])%mod;
    printf("%lld\n",ans);
}
int main()
{
    init();
    work();
    return 0;
}

四.反思总结

本周训练的内容,涉及的算法较多,暴露出我的问题如:

        1.对一些算法的掌握程度还不够好,导致做题卡顿。需要加强的还有二分,贪心。

        2.掌握的算法知识还不够多,碰到如树状数组的题目没有思路写。需要补充的是树状数组,位运算,拓扑排序,最短路,树状DP,动态规划DP,哈希。

对于问题1:重新复习一遍这些算法,自己实现代码模板复现,要求下次碰到这类算法时能迅速想出解题步骤,再把这周涉及这些算法的题重新做过去,直到不卡顿为止。对于问题2:在CSDN和B站上找了相关视频和文章学习这些算法,清楚了算法实现的原理和步骤,再回头把这些算法的题目做过去,做到能脱离题解写出AC代码,空闲时手写一遍算法思路步骤。

总而言之,本周的训练让我感觉在算法竞赛的道路上又前进了一步,希望自己能坚持下去,以饱满的热情,完成冬训,提高自己的水平,在后面的算法竞赛里拿到好成绩。

  • 46
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值