ICPC 2018 Malaysia(solve10/10)

题目链接:upc oj

A题

题意:给你n个人数到第m个人出来,问你最后一个是谁。

思想:约瑟夫环数学问题(公式)

B题

题意:给你n个城市m条路,然后给你k条路径,必须从u到v,然后问你走完这k条路最小花费是多少,如果不能走通输出-1

思想:先将k个点和其他k-1个点连接其他,这样就是一个最大18个点组成的图。利用状态压缩去求解即可。

考虑下当前集合中已经有的点去连接集合中没有的点,需要建立一条边是连接上

dp[i][j]代表那个u到另外的v的距离 。D[i][j]代表当前i这个集合中都在了 而且终点是j这个地方的最小的那个花费

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
struct node{
    int to;
    int next;
    ll valu;
}no[4*maxn];
int head[maxn];
int vis[maxn];
ll dist[maxn];
int cnt,n;
struct Node{
    int u;
    int v;
}No[20];
void add(int u,int v,ll valu)
{
    no[cnt].to=v;
    no[cnt].valu=valu;
    no[cnt].next=head[u];
    head[u]=cnt++;
}
void spfa(int s){  
    for(int i=0;i<=n;i++)
    {
        vis[i]=0;
        dist[i]=1e18;
    }
    queue<int> Q;  
    Q.push(s);  
    vis[s]=1;  
    dist[s]=0;  
    while (!Q.empty()){  
        int u=Q.front();  
        Q.pop();  
        vis[u]=0;  
        for (int i=head[u];i!=-1;i=no[i].next){  
            int v=no[i].to;  
            if(dist[v]>dist[u]+no[i].valu){  
                dist[v]=dist[u]+no[i].valu;  
                if (!vis[v]){  
                    vis[v]=1;  
                    Q.push(v);  
                }  
            }  
        }  
    }  
}  
ll dp[20][20];//记录边和边的最短距离 
ll D[270000][20];//状态压缩 
int main()
{
    cnt=0;
    memset(head,-1,sizeof(head)); 
    int m,k,a,b;
    ll c;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%lld",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    for(int i=1;i<=k;i++)
        scanf("%d%d",&No[i].u,&No[i].v);
          
    for(int i=0;i<20;i++)
        for(int j=0;j<20;j++)
            dp[i][j]=1e18; 
              
    for(int i=1;i<=k;i++)
    {
        spfa(No[i].u);
        for(int j=1;j<=k;j++)
            dp[i][j]=dist[No[j].v];
    }
      
    for(int i=0;i<(1<<k);i++)
        for(int j=0;j<=k;j++) 
            D[i][j]=1e18;
      
    for(int i=0;i< k ;i++)//初始状态都是本身的开头到本身的结尾 
        D[1<<i][i] = dp[i+1][i+1];
      
    ll ans=0x3f3f3f3f;
    for(int i=1;i<(1<<k);i++)//枚举所有的状态 
    {
        for(int j=0;j<k;j++)
        {
            if(!(i&(1<<j)))//需要考虑是当前状态的点 
                continue;
            for(int kk=0;kk<k;kk++)
            {
                if(i&(1<<kk))//需要是不存在的状态 
                    continue; 
                D[i|(1<<kk)][kk] = min(D[i|(1<<kk)][kk] , D[i][j]+dp[j+1][kk+1]+dp[kk+1][kk+1]); 
            }
            if(i+1 == (1<<k))
                ans=min(ans,D[i][j]); 
        }
    } 
    if(ans==0x3f3f3f3f)
        printf("-1\n");
    else
        printf("%lld\n",ans);
    return 0;
}

C题

题意:给你一个n,然后问你有多少序列保证一个集合2个数学不会小于等于1,然后起点从1或者2开始。

思想:dfs打表看规律。

D题

题意:给你n个点,m条边,问你n个点联通最小花费

思想:最小生成树

E题

题意:给你2个矩阵问你是否可以乘。

思想:模拟

F题

题意:给你n*n的格子可以分成多少个矩形。

思想:手写几项就发现了规律。数太大所以java

G题

题意:给你3种肉,每种肉1kg可以制作多少沙爹,然后给你每样买的数量和米饭的数量。问你最大利润

思想:模拟

H题

题意:没听懂 队友过的  

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1005;
int a[maxn];
int mp[5][5];
int main()
{
    int T;
    scanf("%d", &T);
    for (int i = 1; i <= T; i++) {
        memset(mp, 0, sizeof(mp));
        int n = 0;
        while (1){
            scanf("%d", &a[n]);
            if (a[n] == 0)
                break;
            int i = a[n]/10, j = a[n] % 10;
            mp[i][j]++;
            n++;
        }
        int flag = 1;
        for (int i = 2; i < n; i++){
            if (a[i] % 10 == a[i - 1] % 10 && a[i] % 10 == a[i - 2] % 10){
                flag = 0;
                break;
            } else if(a[i] / 10 == a[i - 1] / 10 && a[i] / 10 == a[i - 2] / 10) {
                flag = 0;
                break;
            }
        }
        if (flag == 0) {
            printf("Case #%d: Not Stroop\n", i);
            continue;
        }
 
        for(int j = 1; j <= 4; j++){
            int sum = 0;
            for(int i = 1; i <= 4; i++){
                if(i != j)
                    sum += mp[i][j];
            }
            if(mp[j][j] != sum){
                flag = 0;
                break;
            }
        }
        if (flag == 0) {
            printf("Case #%d: Not Stroop\n", i);
            continue;
        }
 
        for(int i = 1; i <= 3; i++){
            for(int j = i + 1; j <= 4; j++){
                if(mp[i][j] != mp[j][i]){
                    flag = 0;
                    break;
                }
            }
            if(flag == 0)
                break;
        }
        if (flag == 0) {
            printf("Case #%d: Not Stroop\n", i);
            continue;
        }
 
        printf("Case #%d: Stroop\n", i);
 
 
    }
    return 0;
}
 

I题

题意:给你n个工厂,每个工厂都有可以制造的类型和回收的类型和将货物转给其他工厂的花费。给你一个序列问你从外到内生产和从内到外的回收费用的和的最小值是多少。当全部制造完成之后会收到的时候可以从任意工厂回收。

思想:动态规划。DP[i][j]代表第i个工厂生产第j件物品的最小花费 。回收一样  最后求一个和即可

#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
typedef long long ll;
int a[maxn][maxn];//转运花费 
int b[maxn][maxn];//生产花费 
int c[maxn][maxn];//回收话费
int d[maxn];//n层 
ll dp[maxn][maxn];
int main()
{
	int n,m,q;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
		for(int j=1;j<=m;j++)
			scanf("%d",&b[i][j]);
		for(int j=1;j<=m;j++)
			scanf("%d",&c[i][j]);
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
		scanf("%d",&d[i]);
	for(int i=0;i<=503;i++)
		for(int j=0;j<=503;j++)
			dp[i][j]=1ll<<60;
	for(int i=0;i<=n;i++)//最后一个 
		dp[i][q+1]=0;//需要将0也初始化,因为第一个物品是不需要转运的费用		
	for(int i=q;i>=1;i--)
	{
		int x=d[i];//当前需要生产的 
		int y=d[i+1];//上一个在哪里生产的
		for(int j=1;j<=n;j++)
		{
			if(b[j][x]==-1)
				continue;
			for(int k=0;k<=n;k++)
			{
				if(b[k][y]==-1)
					continue;
				dp[j][i]=min(dp[j][i],dp[k][i+1]+b[j][x]+a[k][j]); 
			}
		}
	}	
	long long ans1=1ll<<60;
	for(int i=1;i<=n;i++)
		ans1=min(ans1,dp[i][1]);
	for(int i=0;i<=503;i++)
		for(int j=0;j<=503;j++)
			dp[i][j]=1ll<<60;
	for(int i=0;i<=n;i++)//反过来 
		dp[i][0]=0;//同转运一样
		
	for(int i=1;i<=q;i++)
	{
		int x=d[i];//当前需要生产的 
		int y=d[i-1];//上一个在哪里生产的
		for(int j=1;j<=n;j++)
		{
			if(c[j][x]==-1)
				continue;
			for(int k=0;k<=n;k++)
			{
				if(c[k][y]==-1)
					continue;
				dp[j][i]=min(dp[j][i],dp[k][i-1]+c[j][x]+a[k][j]); 
			}
		}
	}	
	long long ans2=1ll<<60; 
	for(int i=1;i<=n;i++)
		ans2=min(ans2,dp[i][q]);
	printf("%lld\n",ans1+ans2);
	return 0;
} 

J题

题意:给你一个时间和感染的人数,让你求一个时间的感染人数。

思想:从已知的条件推出来递增式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值